Skip to content

Commit

Permalink
feat: Windows support for cloud builder (#6211)
Browse files Browse the repository at this point in the history
* feat: Windows support for cloud builder

At the moment this implementation is mysteriously 5-20 seconds slower per build than the nsc-proxy implementation. There seems to be an additional cost of establishing the connection.

Co-authored-by: Anna Mager <twelvemo@users.noreply.github.com>

* chore: send platforms and actionVersion in build request

* chore: fix unit tests

---------

Co-authored-by: Anna Mager <twelvemo@users.noreply.github.com>
Co-authored-by: Anna Mager <anna@garden.io>
Co-authored-by: Anna Mager <78752267+twelvemo@users.noreply.github.com>
  • Loading branch information
4 people committed Jun 28, 2024
1 parent b33d9c8 commit 14a90ac
Show file tree
Hide file tree
Showing 10 changed files with 384 additions and 209 deletions.
61 changes: 38 additions & 23 deletions core/src/cloud/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export interface CloudEnvironment {
export interface CloudProject {
id: string
name: string
organizationId: string
repositoryUrl: string
environments: CloudEnvironment[]
}
Expand All @@ -164,9 +165,7 @@ export interface GetSecretsParams {
environmentName: string
}

function toCloudProject(
project: GetProjectResponse["data"] | ListProjectsResponse["data"][0] | CreateProjectsForRepoResponse["data"][0]
): CloudProject {
function toCloudProject(project: GetProjectResponse["data"] | CreateProjectsForRepoResponse["data"][0]): CloudProject {
const environments: CloudEnvironment[] = []

for (const environment of project.environments) {
Expand All @@ -176,6 +175,7 @@ function toCloudProject(
return {
id: project.id,
name: project.name,
organizationId: project.organization.id,
repositoryUrl: project.repositoryUrl,
environments,
}
Expand Down Expand Up @@ -423,12 +423,7 @@ export class CloudApi {
return undefined
}

const project = toCloudProject(projectList[0])

// Cache the entry by ID
this.projects.set(project.id, project)

return project
return await this.getProjectById(projectList[0].id)
}

async createProject(projectName: string): Promise<CloudProject> {
Expand Down Expand Up @@ -988,19 +983,30 @@ export class CloudApi {
return { results, errors }
}

async registerCloudBuilderBuild(body: {
async registerCloudBuilderBuild({
organizationId,
...body
}: {
organizationId: string
actionName: string
actionUid: string
actionVersion: string
coreSessionId: string
platforms: string[]
mtlsClientPublicKeyPEM: string | undefined
}): Promise<RegisterCloudBuilderBuildResponse> {
try {
return await this.post<RegisterCloudBuilderBuildResponse>(`/cloudbuilder/builds/`, {
body,
})
return await this.post<RegisterCloudBuilderBuildResponse>(
`/organizations/${organizationId}/cloudbuilder/builds/`,
{
body,
}
)
// TODO: error handling
} catch (err) {
return {
data: {
version: "v1",
version: "v2",
availability: {
available: false,
reason: `Failed to determine Garden Cloud Builder availability: ${extractErrorMessageBodyFromGotError(err) ?? err}`,
Expand Down Expand Up @@ -1036,10 +1042,10 @@ export class CloudApi {
}

// TODO(cloudbuilder): import these from api-types
type V1RegisterCloudBuilderBuildResponse = {
type RegisterCloudBuilderBuildResponseV2 = {
data: {
version: "v1"
availability: CloudBuilderAvailability
version: "v2"
availability: CloudBuilderAvailabilityV2
}
}
type UnsupportedRegisterCloudBuilderBuildResponse = {
Expand All @@ -1048,16 +1054,25 @@ type UnsupportedRegisterCloudBuilderBuildResponse = {
}
}
type RegisterCloudBuilderBuildResponse =
| V1RegisterCloudBuilderBuildResponse
| RegisterCloudBuilderBuildResponseV2
| UnsupportedRegisterCloudBuilderBuildResponse

export type CloudBuilderAvailable = {
export type CloudBuilderAvailableV2 = {
available: true
token: string
region: "eu" // location of the builder. Currently only eu is supported

buildx: {
endpoints: {
platform: string
mtlsEndpoint: string
serverCaPem: string
}[]
clientCertificatePem: string
// only defined if the request did not include a "mtlsClientPublicKeyPEM"
privateKeyPem: string | undefined
}
}
export type CloudBuilderNotAvailable = {
export type CloudBuilderNotAvailableV2 = {
available: false
reason: string
}
export type CloudBuilderAvailability = CloudBuilderAvailable | CloudBuilderNotAvailable
export type CloudBuilderAvailabilityV2 = CloudBuilderAvailableV2 | CloudBuilderNotAvailableV2
10 changes: 4 additions & 6 deletions core/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1934,11 +1934,6 @@ export const resolveGardenParams = profileAsync(async function _resolveGardenPar
commandName: opts.commandInfo.name,
})

// If the user is logged in and a cloud project exists we use that ID
// but fallback to the one set in the config (even if the user isn't logged in).
// Same applies for domains.
const projectId = cloudProject?.id || config.id

config = resolveProjectConfig({
log,
defaultEnvironmentName: configDefaultEnvironment,
Expand Down Expand Up @@ -2006,7 +2001,10 @@ export const resolveGardenParams = profileAsync(async function _resolveGardenPar
artifactsPath,
vcsInfo,
sessionId,
projectId,
// If the user is logged in and a cloud project exists we use that ID
// but fallback to the one set in the config (even if the user isn't logged in).
// Same applies for domains.
projectId: cloudProject?.id || config.id,
cloudDomain,
projectConfig: config,
projectRoot,
Expand Down
3 changes: 3 additions & 0 deletions core/src/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type WrappedFromGarden = Pick<
| "gardenDirPath"
| "workingCopyId"
| "cloudApi"
| "projectId"
// TODO: remove this from the interface
| "environmentName"
| "namespace"
Expand Down Expand Up @@ -85,6 +86,7 @@ export const pluginContextSchema = createSchema({
.description("Indicate if the current environment is a production environment.")
.example(true),
projectName: projectNameSchema(),
projectId: joi.string().optional().description("The unique ID of the current project."),
projectRoot: joi.string().description("The absolute path of the project root."),
projectSources: projectSourcesSchema(),
provider: providerSchema().description("The provider being used for this context.").id("ctxProviderSchema"),
Expand Down Expand Up @@ -224,5 +226,6 @@ export async function createPluginContext({
tools: await garden.getTools(),
workingCopyId: garden.workingCopyId,
cloudApi: garden.cloudApi,
projectId: garden.projectId,
}
}
6 changes: 3 additions & 3 deletions core/src/plugins/container/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import {
import type { Writable } from "stream"
import type { ActionLog } from "../../logger/log-entry.js"
import type { PluginContext } from "../../plugin-context.js"
import type { SpawnOutput } from "../../util/util.js"
import { cloudBuilder } from "./cloudbuilder.js"
import { styles } from "../../logger/styles.js"
import type { CloudBuilderAvailable } from "../../cloud/api.js"
import type { CloudBuilderAvailableV2 } from "../../cloud/api.js"
import type { SpawnOutput } from "../../util/util.js"

export const validateContainerBuild: BuildActionHandler<"validate", ContainerBuildAction> = async ({ action }) => {
// configure concurrency limit for build status task nodes.
Expand Down Expand Up @@ -206,7 +206,7 @@ const BUILDKIT_LAYER_CACHED_REGEX = /^#[0-9]+ CACHED/

async function buildContainerInCloudBuilder(params: {
action: Resolved<ContainerBuildAction>
availability: CloudBuilderAvailable
availability: CloudBuilderAvailableV2
outputStream: Writable
timeout: number
log: ActionLog
Expand Down
Loading

0 comments on commit 14a90ac

Please sign in to comment.