diff --git a/integrations/kubernetes-job-agent/src/index.ts b/integrations/kubernetes-job-agent/src/index.ts index 014aa75b..fba861b0 100644 --- a/integrations/kubernetes-job-agent/src/index.ts +++ b/integrations/kubernetes-job-agent/src/index.ts @@ -38,7 +38,6 @@ const deployManifest = async ( updateJobRequest: { status: "invalid_job_agent", message: "Job name not found in manifest.", - }, }); return; } @@ -51,7 +50,6 @@ const deployManifest = async ( status: "in_progress", externalId: `${namespace}/${name}`, message: "Job created successfully.", - }, }); logger.info(`Job created successfully`, { jobId, diff --git a/integrations/terraform-cloud-scanner/src/__tests__/scanner.test.ts b/integrations/terraform-cloud-scanner/src/__tests__/scanner.test.ts index 573bd354..5a854aa1 100644 --- a/integrations/terraform-cloud-scanner/src/__tests__/scanner.test.ts +++ b/integrations/terraform-cloud-scanner/src/__tests__/scanner.test.ts @@ -46,12 +46,25 @@ describe("Scanner Module", () => { vi.spyOn(env, "CTRLPLANE_SCANNER_NAME", "get").mockReturnValue( "mock-scanner", ); + vi.spyOn(env, "CTRLPLANE_WORKSPACE_TARGET_NAME", "get").mockReturnValue( + "{{workspace.attributes.name}}", + ); const mockWorkspaces = [ { id: "workspace-1", type: "workspaces", - attributes: { name: "Workspace-One", "tag-names": ["prod"] }, + attributes: { + name: "Workspace-One", + "tag-names": ["prod", "env:staging"], + "auto-apply": true, + "terraform-version": "1.0.0", + "vcs-repo": { + identifier: "org/repo", + branch: "main", + "repository-http-url": "https://github.com/org/repo", + }, + }, }, ]; @@ -67,79 +80,100 @@ describe("Scanner Module", () => { sensitive: false, }, }, - { - id: "var-2", - type: "vars", - attributes: { - key: "ENV_VAR", - value: "env_value", - category: "env", - hcl: false, - sensitive: false, - }, - }, ]; - const mockProviderId = "provider-123"; - vi.mocked(listWorkspaces).mockResolvedValue(mockWorkspaces as any); vi.mocked(listVariables).mockResolvedValue(mockVariables as any); - vi.spyOn(api, "setTargetProvidersTargets").mockResolvedValue(undefined); - vi.spyOn(api, "upsertTargetProvider").mockResolvedValue({ - id: mockProviderId, - name: "mock-provider-name", - workspaceId: "ctrlplane-workspace", + vi.spyOn(api, "GET").mockResolvedValue({ + data: { + id: "provider-123", + name: "mock-provider-name", + workspaceId: "36427c59-e2bd-4b3f-bf54-54404ef6aa0e", + }, + status: 200, + statusText: "OK", + headers: {}, + config: {} as any, + }); + + const patchMock = vi.spyOn(api, "PATCH").mockResolvedValue({ + data: { + "application/json": { + id: "mock-id", + name: "mock-name", + workspaceId: "mock-workspace-id", + kind: "mock-kind", + identifier: "mock-identifier", + version: "mock-version", + config: {}, + metadata: {}, + }, + }, + response: new Response(), }); await scan(); - expect(() => listVariables("workspace-1")).not.toThrow(); - expect(() => - api.upsertTargetProvider({ - workspaceId: "ctrlplane-workspace", - name: "mock-scanner", - }), - ).not.toThrow(); - - expect(() => listWorkspaces()).not.toThrow(); - expect(() => listVariables("workspace-1")).not.toThrow(); - expect(() => - api.upsertTargetProvider({ - workspaceId: "ctrlplane-workspace", - name: "mock-scanner", - }), - ).not.toThrow(); + expect(listWorkspaces).toHaveBeenCalled(); + expect(listVariables).toHaveBeenCalledWith("workspace-1"); - expect(() => - api.setTargetProvidersTargets({ - providerId: mockProviderId, - setTargetProvidersTargetsRequest: { + expect(patchMock).toHaveBeenCalledWith( + "/v1/target-providers/{providerId}/set", + expect.objectContaining({ + body: { targets: [ { version: "terraform/v1", kind: "Workspace", - name: "workspace-Workspace-One", + name: "mock-workspace-target-name", identifier: "workspace-1", config: { workspaceId: "workspace-1", }, metadata: { - "terraform/organization": "mock-org", - "terraform/workspace-name": "Workspace-One", - "var/TF_VAR_example": "example_value", - "env/ENV_VAR": "env_value", - "tags/prod": "true", - "ctrlplane/link": expect.stringContaining( - "https://app.terraform.io/app/mock-org/workspaces/Workspace-One", - ), + "ctrlplane/external-id": "workspace-1", + "ctrlplane/links": + '{"Terraform Workspace":"https://app.terraform.io/app/mock-org/workspaces/Workspace-One"}', + "terraform-cloud/organization": "mock-org", + "terraform-cloud/tag/env": "staging", + "terraform-cloud/tag/prod": "true", + "terraform-cloud/variables/TF_VAR_example": "example_value", + "terraform-cloud/vcs-repo/branch": "main", + "terraform-cloud/vcs-repo/identifier": "org/repo", + "terraform-cloud/vcs-repo/repository-http-url": + "https://github.com/org/repo", + "terraform-cloud/workspace-auto-apply": "true", + "terraform-cloud/workspace-name": "Workspace-One", + "terraform/version": "1.0.0", }, }, ], }, + params: { + path: { + providerId: "provider-123", + }, + }, }), - ).not.toThrow(); + ); expect(logger.info).toHaveBeenCalledWith("Successfully registered targets"); }); + + it("should handle scan errors gracefully", async () => { + vi.mocked(listWorkspaces).mockRejectedValue(new Error("API Error")); + + const mockExit = vi + .spyOn(process, "exit") + .mockImplementation(() => undefined as never); + + await scan(); + + expect(logger.error).toHaveBeenCalledWith( + "An error occurred during the scan process:", + expect.any(Error), + ); + expect(mockExit).toHaveBeenCalledWith(1); + }); }); diff --git a/integrations/terraform-cloud-scanner/src/scanner.ts b/integrations/terraform-cloud-scanner/src/scanner.ts index ab03f660..0bd0a0a8 100644 --- a/integrations/terraform-cloud-scanner/src/scanner.ts +++ b/integrations/terraform-cloud-scanner/src/scanner.ts @@ -1,8 +1,8 @@ -import type { SetTargetProvidersTargetsRequestTargetsInner } from "@ctrlplane/node-sdk"; import handlebars from "handlebars"; import _ from "lodash"; import { logger } from "@ctrlplane/logger"; +import { TargetProvider } from "@ctrlplane/node-sdk"; import type { Variable, Workspace } from "./types.js"; import { listVariables, listWorkspaces } from "./api.js"; @@ -17,21 +17,27 @@ const workspaceTemplate = handlebars.compile( * Scans Terraform Cloud workspaces and registers them as targets with prefixed labels and a link. */ export async function scan() { + const scanner = new TargetProvider( + { + workspaceId: env.CTRLPLANE_WORKSPACE_ID, + name: env.CTRLPLANE_SCANNER_NAME, + }, + api, + ); logger.info("Starting Terraform Cloud scan"); try { - const providerId = await getOrCreateProviderId(); - if (!providerId) { - logger.error( - "Provider ID is not available. Aborting target registration.", - ); - process.exit(1); - } + const provider = await scanner.get(); + + logger.info(`Scanner ID: ${provider.id}`, { id: provider.id }); + logger.info("Running Terrafrom Cloud scanner", { + date: new Date().toISOString(), + }); const workspaces: Workspace[] = await listWorkspaces(); logger.info(`Found ${workspaces.length} workspaces`); - const targets: SetTargetProvidersTargetsRequestTargetsInner[] = []; + const targets = []; for (const workspace of workspaces) { logger.info( @@ -48,7 +54,7 @@ export async function scan() { const link = buildWorkspaceLink(workspace); const targetName = workspaceTemplate({ workspace }); - const target: SetTargetProvidersTargetsRequestTargetsInner = { + const target = { version: "terraform/v1", kind: "Workspace", name: targetName, @@ -78,12 +84,7 @@ export async function scan() { logger.info(`Registering ${uniqueTargets.length} unique targets`); - await api.setTargetProvidersTargets({ - providerId, - setTargetProvidersTargetsRequest: { - targets: uniqueTargets, - }, - }); + await scanner.set(uniqueTargets); logger.info("Successfully registered targets"); } catch (error) { @@ -155,23 +156,3 @@ function buildWorkspaceLink(workspace: Workspace): Record { )}/workspaces/${encodeURIComponent(workspace.attributes.name)}`, }; } - -/** - * Helper function to get or create the provider ID. - * @returns The provider ID as a string or null if failed. - */ -async function getOrCreateProviderId(): Promise { - return api - .upsertTargetProvider({ - workspaceId: env.CTRLPLANE_WORKSPACE_ID, - name: env.CTRLPLANE_SCANNER_NAME, - }) - .then(({ id }) => { - logger.info(`Using provider ID: ${id}`); - return id; - }) - .catch((error) => { - logger.error("Failed to get or create provider ID:", error); - return null; - }); -} diff --git a/integrations/terraform-cloud-scanner/src/sdk.ts b/integrations/terraform-cloud-scanner/src/sdk.ts index e58ea2b8..76533feb 100644 --- a/integrations/terraform-cloud-scanner/src/sdk.ts +++ b/integrations/terraform-cloud-scanner/src/sdk.ts @@ -1,10 +1,8 @@ -import { Configuration, DefaultApi } from "@ctrlplane/node-sdk"; +import { createClient } from "@ctrlplane/node-sdk"; import { env } from "./config.js"; -const config = new Configuration({ - basePath: `${env.CTRLPLANE_BASE_URL}/api`, +export const api = createClient({ + baseUrl: env.CTRLPLANE_BASE_URL, apiKey: env.CTRLPLANE_API_KEY, }); - -export const api = new DefaultApi(config); diff --git a/packages/node-sdk/openapitools.json b/packages/node-sdk/openapitools.json deleted file mode 100644 index bf574c92..00000000 --- a/packages/node-sdk/openapitools.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "../../node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "spaces": 2, - "generator-cli": { - "version": "7.9.0", - "generators": { - "v1": { - "generatorName": "typescript-node", - "output": "#{cwd}/src", - "glob": "../../openapi.v1.json" - } - } - } -}