diff --git a/awsx/ecr/auth.test.ts b/awsx/ecr/auth.test.ts new file mode 100644 index 000000000..7809e2ef2 --- /dev/null +++ b/awsx/ecr/auth.test.ts @@ -0,0 +1,88 @@ +// Copyright 2016-2025, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as pulumi from "@pulumi/pulumi"; + +pulumi.runtime.setMocks( + { + newResource: function (args: pulumi.runtime.MockResourceArgs): { id: string; state: any } { + return { + id: args.inputs.name + "_id", + state: args.inputs, + }; + }, + call: function ( + args: pulumi.runtime.MockCallArgs, + ): pulumi.runtime.MockCallResult | Promise { + return { + authorizationToken: Buffer.from("AWS:password").toString("base64"), + proxyEndpoint: `https://${args.inputs.registryId}.dkr.ecr.us-west-2.amazonaws.com`, + }; + }, + }, + "project", + "stack", + false, // Sets the flag `dryRun`, which indicates if pulumi is running in preview mode. +); + +describe("getDockerCredentials", () => { + let auth: typeof import("./auth"); + + beforeAll(async function () { + auth = await import("./auth"); + }); + + it("should return Docker credentials when valid repositoryUrl is provided", async () => { + const args = { repositoryUrl: "https://123456789012.dkr.ecr.us-west-2.amazonaws.com" }; + const opts = {}; + + const result = await promisify(auth.getDockerCredentials(args, opts)); + + expect(result).toEqual({ + address: "https://123456789012.dkr.ecr.us-west-2.amazonaws.com", + username: "AWS", + password: "password", + }); + }); + + it("should use registryId if provided", async () => { + const args = { + repositoryUrl: "123456789012.dkr.ecr.us-west-2.amazonaws.com", + registryId: "987654321098", + }; + const opts = {}; + + const result = await promisify(auth.getDockerCredentials(args, opts)); + + expect(result).toEqual({ + address: `https://${args.registryId}.dkr.ecr.us-west-2.amazonaws.com`, + username: "AWS", + password: "password", + }); + }); + + it("should throw an error if the repositoryUrl is not a valid URL", async () => { + const args = { repositoryUrl: "foo:bar" }; + const opts = {}; + + expect(() => { + auth.getDockerCredentials(args, opts); + }).toThrow("Repository URL is not a valid URL."); + }); +}); + +function promisify(output: pulumi.Output | undefined): Promise { + expect(output).toBeDefined(); + return new Promise((resolve) => output!.apply(resolve)); +} diff --git a/awsx/ecr/auth.ts b/awsx/ecr/auth.ts new file mode 100644 index 000000000..6096ff7fb --- /dev/null +++ b/awsx/ecr/auth.ts @@ -0,0 +1,111 @@ +// Copyright 2016-2025, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as aws from "@pulumi/aws"; +import * as pulumi from "@pulumi/pulumi"; + +/** + * Arguments for fetching ECR registry credentials. + */ +export interface CredentialArgs { + /** + * The URL of the ECR registry to get credentials for. + * Can be provided with or without the https:// protocol prefix. + */ + repositoryUrl: string; + + /** + * Optional registry ID (AWS account ID) to get credentials for. + * If not provided, will be parsed from the registry URL. + */ + registryId?: string; +} + +/** + * Docker registry credentials for authenticating with an ECR registry. + */ +export interface DockerCredentials { + /** + * The address of the ECR registry. + */ + address: string; + + /** + * The username to authenticate with. For ECR this is typically "AWS". + */ + username: string; + + /** + * The password to authenticate with. For ECR this is a temporary token. + */ + password: string; +} + +/** + * Fetches Docker registry credentials for authenticating with an ECR registry. + * + * @param args Arguments for fetching ECR registry credentials + * @param opts InvokeOutputOptions to use for the credential lookup + * @returns Docker registry credentials including address, username and password + */ +export function getDockerCredentials( + args: CredentialArgs, + opts: pulumi.InvokeOutputOptions, +): pulumi.Output { + let registryId: string; + if (args.registryId) { + registryId = args.registryId; + } else { + // add protocol to help parse the url + const repositoryUrl = args.repositoryUrl?.startsWith("https://") + ? args.repositoryUrl + : "https://" + args.repositoryUrl; + + let parsedUrl: URL; + try { + parsedUrl = new URL(repositoryUrl); + } catch (e) { + throw new pulumi.InputPropertyError({ + reason: `Repository URL is not a valid URL.`, + propertyPath: "repositoryUrl", + }); + } + + const hostnameParts = parsedUrl.hostname.split("."); + if (hostnameParts.length < 1) { + throw new pulumi.InputPropertyError({ + reason: `Could not parse registry ID from Repository URL. It should be in the format of .dkr.ecr..amazonaws.com`, + propertyPath: "repositoryUrl", + }); + } + + // the registry id is the AWS account id. It's the first part of the hostname + registryId = hostnameParts[0]; + } + + const ecrCredentials = aws.ecr.getCredentialsOutput({ registryId: registryId }, opts); + + return ecrCredentials.apply((creds) => { + const decodedCredentials = Buffer.from(creds.authorizationToken, "base64").toString(); + const [username, password] = decodedCredentials.split(":"); + if (!password || !username) { + throw new Error("Invalid credentials"); + } + return { + address: creds.proxyEndpoint, + username: username, + password: password, + }; + }); +} diff --git a/awsx/ecr/image.ts b/awsx/ecr/image.ts index b8a192ffc..4adb421b8 100644 --- a/awsx/ecr/image.ts +++ b/awsx/ecr/image.ts @@ -17,6 +17,7 @@ import * as docker from "@pulumi/docker-build"; import * as pulumi from "@pulumi/pulumi"; import * as schema from "../schema-types"; import * as utils from "../utils"; +import { getDockerCredentials } from "./auth"; export class Image extends schema.Image { constructor(name: string, args: schema.ImageArgs, opts: pulumi.ComponentResourceOptions = {}) { @@ -32,9 +33,6 @@ export function computeImageFromAsset( ) { const { repositoryUrl, registryId: inputRegistryId, imageTag, ...dockerInputs } = args ?? {}; - const url = new URL("https://" + repositoryUrl); // Add protocol to help it parse - const registryId = inputRegistryId ?? url.hostname.split(".")[0]; - pulumi.log.debug(`Building container image at '${JSON.stringify(dockerInputs)}'`, parent); const imageName = args.imageName @@ -50,24 +48,11 @@ export function computeImageFromAsset( // the unique image name we pushed to. The name will change if the image changes ensuring // the TaskDefinition get's replaced IFF the built image changes. - const ecrCredentials = aws.ecr.getCredentialsOutput( - { registryId: registryId }, - { parent, async: true }, + const registryCredentials = getDockerCredentials( + { repositoryUrl: repositoryUrl, registryId: inputRegistryId }, + { parent }, ); - const registryCredentials = ecrCredentials.authorizationToken.apply((authorizationToken) => { - const decodedCredentials = Buffer.from(authorizationToken, "base64").toString(); - const [username, password] = decodedCredentials.split(":"); - if (!password || !username) { - throw new Error("Invalid credentials"); - } - return { - address: ecrCredentials.proxyEndpoint, - username: username, - password: password, - }; - }); - let cacheFrom: docker.types.input.CacheFromArgs[] = []; if (dockerInputs.cacheFrom !== undefined) { cacheFrom = dockerInputs.cacheFrom.map((c) => { diff --git a/awsx/ecr/index.ts b/awsx/ecr/index.ts index 5941ba8a3..681e2ea8a 100644 --- a/awsx/ecr/index.ts +++ b/awsx/ecr/index.ts @@ -14,3 +14,4 @@ export * from "./repository"; export * from "./image"; +export * from "./registryImage"; diff --git a/awsx/ecr/registryImage.ts b/awsx/ecr/registryImage.ts new file mode 100644 index 000000000..789235722 --- /dev/null +++ b/awsx/ecr/registryImage.ts @@ -0,0 +1,65 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as docker from "@pulumi/docker"; +import * as pulumi from "@pulumi/pulumi"; +import * as schema from "../schema-types"; +import { getDockerCredentials } from "./auth"; + +export class RegistryImage extends schema.RegistryImage { + constructor( + name: string, + args: schema.RegistryImageArgs, + opts?: pulumi.ComponentResourceOptions, + ) { + super(name, args, opts); + + const creds = pulumi + .output(args.repositoryUrl) + .apply((url) => getDockerCredentials({ repositoryUrl: url }, { parent: this })); + const provider = new docker.Provider(name, { + registryAuth: [creds], + }); + + // tag the source image in the form of : + // we explicitly look up the source image in order to trigger a push whenever the source image changes + const sourceImage = docker.getRemoteImageOutput( + { name: args.sourceImage }, + { parent: this, provider }, + ); + const tagName = args.tag ? args.tag : "latest"; + const tag = new docker.Tag( + name, + { + sourceImage: sourceImage.id, + targetImage: pulumi.interpolate`${args.repositoryUrl}:${tagName}`, + }, + { parent: this, provider }, + ); + + this.image = new docker.RegistryImage( + name, + { + ...args, + name: tag.targetImage, + triggers: { + ...args.triggers, + // trigger a push whenever the source image changes + "@pulumi/awsx/internal/sourceImage": sourceImage.id, + }, + }, + { parent: this, provider }, + ); + } +} diff --git a/awsx/package.json b/awsx/package.json index 8c565e472..4bfcdab63 100644 --- a/awsx/package.json +++ b/awsx/package.json @@ -25,10 +25,11 @@ "//": "Pulumi sub-provider dependencies must be pinned at an exact version because we extract this value to generate the correct dependency in the schema", "dependencies": { "@pulumi/aws": "6.66.2", + "@pulumi/docker": "4.6.0", "@pulumi/docker-build": "0.0.8", "@pulumi/pulumi": "3.144.1", "@types/aws-lambda": "^8.10.23", - "docker-classic": "npm:@pulumi/docker@4.5.8", + "docker-classic": "npm:@pulumi/docker@4.6.0", "ip-address": "^8.1.0", "mime": "^3.0.0", "netmask": "^2.0.2" diff --git a/awsx/resources.ts b/awsx/resources.ts index fdac2ac98..a6c1e0125 100644 --- a/awsx/resources.ts +++ b/awsx/resources.ts @@ -15,8 +15,7 @@ import * as pulumi from "@pulumi/pulumi"; import { Trail } from "./cloudtrail"; import * as ec2 from "./ec2"; -import { Repository } from "./ecr"; -import { Image } from "./ecr/image"; +import { Image, RegistryImage, Repository } from "./ecr"; import * as ecs from "./ecs"; import * as lb from "./lb"; import * as schemaTypes from "./schema-types"; @@ -34,6 +33,7 @@ const resources: schemaTypes.ResourceConstructor = { "awsx:ec2:DefaultVpc": (...args) => new ec2.DefaultVpc(...args), "awsx:ecr:Repository": (...args) => new Repository(...args), "awsx:ecr:Image": (...args) => new Image(...args), + "awsx:ecr:RegistryImage": (...args) => new RegistryImage(...args), }; export function construct( diff --git a/awsx/schema-types.ts b/awsx/schema-types.ts index bb8c88b2d..4ee53dd10 100644 --- a/awsx/schema-types.ts +++ b/awsx/schema-types.ts @@ -10,6 +10,7 @@ export type ResourceConstructor = { readonly "awsx:ec2:DefaultVpc": ConstructComponent; readonly "awsx:ec2:Vpc": ConstructComponent; readonly "awsx:ecr:Image": ConstructComponent; + readonly "awsx:ecr:RegistryImage": ConstructComponent; readonly "awsx:ecr:Repository": ConstructComponent; readonly "awsx:ecs:EC2Service": ConstructComponent; readonly "awsx:ecs:EC2TaskDefinition": ConstructComponent; @@ -23,6 +24,7 @@ export type Functions = { "awsx:ec2:getDefaultVpc": (inputs: getDefaultVpcInputs) => Promise; }; import * as aws from "@pulumi/aws"; +import * as docker from "@pulumi/docker"; export abstract class Trail extends (pulumi.ComponentResource) { public bucket?: aws.s3.Bucket | pulumi.Output; public logGroup?: aws.cloudwatch.LogGroup | pulumi.Output; @@ -118,6 +120,20 @@ export interface ImageArgs { readonly repositoryUrl: pulumi.Input; readonly target?: pulumi.Input; } +export abstract class RegistryImage extends (pulumi.ComponentResource) { + public image!: docker.RegistryImage | pulumi.Output; + constructor(name: string, args: pulumi.Inputs, opts: pulumi.ComponentResourceOptions = {}) { + super("awsx:ecr:RegistryImage", name, opts.urn ? { image: undefined } : { name, args, opts }, opts); + } +} +export interface RegistryImageArgs { + readonly insecureSkipVerify?: pulumi.Input; + readonly keepRemotely?: pulumi.Input; + readonly repositoryUrl: pulumi.Input; + readonly sourceImage: pulumi.Input; + readonly tag?: pulumi.Input; + readonly triggers?: pulumi.Input>>; +} export abstract class Repository extends (pulumi.ComponentResource) { public lifecyclePolicy?: aws.ecr.LifecyclePolicy | pulumi.Output; public repository!: aws.ecr.Repository | pulumi.Output; diff --git a/awsx/scripts/generate-provider-types.ts b/awsx/scripts/generate-provider-types.ts index e1c92baff..b5682509b 100644 --- a/awsx/scripts/generate-provider-types.ts +++ b/awsx/scripts/generate-provider-types.ts @@ -32,6 +32,7 @@ const externalRefs = (() => { }; }; addRef("aws"); + addRef("docker"); return externalRefs; })(); @@ -81,6 +82,10 @@ const resolveRef = (ref: unknown, direction: Direction): ts.TypeNode => { const resourceName = typeParts[2]; const path = decodeURIComponent(typeParts[1]).split("/"); path.pop(); // Last section is same as the resource name + if (path.length === 1 && path[0] === "index") { + // this is the default module name, so we don't need to include it + path.pop(); + } return ts.factory.createTypeReferenceNode( [externalRef.name, ...path, resourceName].join("."), ); diff --git a/awsx/yarn.lock b/awsx/yarn.lock index e68871861..50c48defd 100644 --- a/awsx/yarn.lock +++ b/awsx/yarn.lock @@ -1676,6 +1676,14 @@ dependencies: "@pulumi/pulumi" "^3.136.0" +"@pulumi/docker@4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@pulumi/docker/-/docker-4.6.0.tgz#127a0c69a06b47a9418dce12cc70a181ff680284" + integrity sha512-9XBQtKGYQykJgXS3nWXk/mp7vSZ4kpwGJcp8yPMwvlUDiIDS4lLPp7ieOJl/qVETXkW8EtQ8qxlJ/n8kTBgiBA== + dependencies: + "@pulumi/pulumi" "^3.142.0" + semver "^5.4.0" + "@pulumi/pulumi@3.144.1", "@pulumi/pulumi@^3.136.0", "@pulumi/pulumi@^3.142.0": version "3.144.1" resolved "https://registry.yarnpkg.com/@pulumi/pulumi/-/pulumi-3.144.1.tgz#96b3c54879f7bc5857ba38ac389e2b7262e44f9e" @@ -2674,10 +2682,10 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -"docker-classic@npm:@pulumi/docker@4.5.8": - version "4.5.8" - resolved "https://registry.yarnpkg.com/@pulumi/docker/-/docker-4.5.8.tgz#55cefdebcee55eedf0674352ab7f6b8101420c30" - integrity sha512-h5ZfsXTt5GaqenOmleNAJT/zXLErYXYMftgFNbTS4Z1n1gQXwBewxZ/p7nEqKZkh0JjZZuoDlRN1+lkosM5W6w== +"docker-classic@npm:@pulumi/docker@4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@pulumi/docker/-/docker-4.6.0.tgz#127a0c69a06b47a9418dce12cc70a181ff680284" + integrity sha512-9XBQtKGYQykJgXS3nWXk/mp7vSZ4kpwGJcp8yPMwvlUDiIDS4lLPp7ieOJl/qVETXkW8EtQ8qxlJ/n8kTBgiBA== dependencies: "@pulumi/pulumi" "^3.142.0" semver "^5.4.0" diff --git a/examples/examples_nodejs_test.go b/examples/examples_nodejs_test.go index 0c494329e..09c83e692 100644 --- a/examples/examples_nodejs_test.go +++ b/examples/examples_nodejs_test.go @@ -27,10 +27,12 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecr" + ecrTypes "github.com/aws/aws-sdk-go-v2/service/ecr/types" "github.com/pulumi/providertest/pulumitest" "github.com/pulumi/providertest/pulumitest/optnewstack" "github.com/pulumi/providertest/pulumitest/opttest" "github.com/pulumi/pulumi/pkg/v3/testing/integration" + "github.com/pulumi/pulumi/sdk/v3/go/auto/optpreview" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -410,20 +412,8 @@ func TestDockerUpgrade(t *testing.T) { t.Logf("Verifying images in ECR repository %q", repoName) client := createEcrClient(t) - describeImagesInput := &ecr.DescribeImagesInput{ - RepositoryName: aws.String(repoName), - } - - var describeImagesOutput *ecr.DescribeImagesOutput - var err error - for retries := 0; retries < 10; retries++ { - describeImagesOutput, err = client.DescribeImages(context.TODO(), describeImagesInput) - require.NoError(t, err, "failed to describe images") - if len(describeImagesOutput.ImageDetails) >= 2 { - break - } - time.Sleep(5 * time.Second) - } + describeImagesOutput, err := getEcrImageDetails(t, client, repoName, 2) + require.NoError(t, err, "failed to describe images") require.NotEmpty(t, describeImagesOutput.ImageDetails, "image details should not be empty") require.Len(t, describeImagesOutput.ImageDetails, 2, "should have 2 images") @@ -453,6 +443,115 @@ func TestDockerUpgrade(t *testing.T) { assert.Truef(t, foundUpdated, "updated image digest %q should exist in ECR", updatedDigest) } +func TestEcrRegistryImage(t *testing.T) { + t.Parallel() + cwd := getCwd(t) + options := []opttest.Option{ + opttest.LocalProviderPath("awsx", filepath.Join(cwd, "..", "bin")), + opttest.YarnLink("@pulumi/awsx"), + } + pt := pulumitest.NewPulumiTest(t, filepath.Join(cwd, "ts-ecr-registry-image"), options...) + + pt.SetConfig(t, "message", "Hello Pulumi!") + + t.Log("Running `pulumi preview` with message=Hello Pulumi!") + previewResult := pt.Preview(t) + t.Logf("Preview:\n%s", previewResult.StdOut) + + t.Log("Running `pulumi up` with message=Hello Pulumi!") + upResult := pt.Up(t) + t.Logf("Up:\n%s", upResult.StdOut) + + require.Contains(t, upResult.Outputs, "latestImageDigest", "latestImageDigest should be in the outputs") + latestImageDigest := upResult.Outputs["latestImageDigest"].Value.(string) + require.NotEmpty(t, latestImageDigest, "latestImageDigest should not be empty") + + require.Contains(t, upResult.Outputs, "repositoryName", "repositoryName should be in the outputs") + repoName := upResult.Outputs["repositoryName"].Value.(string) + require.NotEmpty(t, repoName, "repositoryName should not be empty") + + client := createEcrClient(t) + describeImagesOutput, err := getEcrImageDetails(t, client, repoName, 1) + require.NoError(t, err, "failed to describe images") + require.NotEmpty(t, describeImagesOutput.ImageDetails, "image details should not be empty") + require.Len(t, describeImagesOutput.ImageDetails, 1, "should have 1 image") + + firstImage := describeImagesOutput.ImageDetails[0] + tags := firstImage.ImageTags + require.Len(t, tags, 3, "image should have 3 tags") + require.Contains(t, tags, "test", "test tag should be in the image tags") + require.Contains(t, tags, "v1.0.0", "v1.0.0 tag should be in the image tags") + require.Contains(t, tags, "latest", "latest tag should be in the image tags") + + // This should produce a new image + pt.SetConfig(t, "message", "Hello Pulumi! (Again...)") + + t.Log("Running `pulumi preview` with message=Hello Pulumi! (Again...)") + previewResult = pt.Preview(t) + t.Logf("Preview:\n%s", previewResult.StdOut) + + t.Log("Running `pulumi up` with message=Hello Pulumi! (Again...)") + upResult = pt.Up(t) + t.Logf("Up:\n%s", upResult.StdOut) + + require.Contains(t, upResult.Outputs, "latestImageDigest", "latestImageDigest should be in the outputs") + latestImageDigest = upResult.Outputs["latestImageDigest"].Value.(string) + require.NotEmpty(t, latestImageDigest, "latestImageDigest should not be empty") + + describeImagesOutput, err = getEcrImageDetails(t, client, repoName, 2) + require.NoError(t, err, "failed to describe images") + require.NotEmpty(t, describeImagesOutput.ImageDetails, "image details should not be empty") + require.Len(t, describeImagesOutput.ImageDetails, 2, "should have 2 images") + + var newImage *ecrTypes.ImageDetail + var oldImage *ecrTypes.ImageDetail + for _, img := range describeImagesOutput.ImageDetails { + if *img.ImageDigest != *firstImage.ImageDigest { + newImage = &img + } else { + oldImage = &img + } + } + + require.NotNil(t, newImage, "new image should not be nil") + require.NotNil(t, oldImage, "old image should still exist after the new image was pushed") + + // The tags should've been moved to the new image + require.Len(t, newImage.ImageTags, 3, "new image should have 3 tags") + require.Contains(t, newImage.ImageTags, "test", "the new image should have the test tag") + require.Contains(t, newImage.ImageTags, "v1.0.0", "the new image should have the v1.0.0 tag") + require.Contains(t, newImage.ImageTags, "latest", "the new image should have the latest tag") + + require.Empty(t, oldImage.ImageTags, "old image should have no tags after the new image was pushed") + + t.Log("Re-running `pulumi preview` to verify that no changes are detected") + previewResult = pt.Preview(t, optpreview.ExpectNoChanges()) + t.Logf("Preview:\n%s", previewResult.StdOut) +} + +func getEcrImageDetails(t *testing.T, client *ecr.Client, repositoryName string, expectedImages int) (*ecr.DescribeImagesOutput, error) { + describeImagesInput := &ecr.DescribeImagesInput{ + RepositoryName: aws.String(repositoryName), + } + + var describeImagesOutput *ecr.DescribeImagesOutput + var err error + maxRetries := 4 + for retries := 0; retries < maxRetries; retries++ { + err = nil + describeImagesOutput, err = client.DescribeImages(context.TODO(), describeImagesInput) + if err != nil { + t.Logf("failed to describe images: %v", err) + continue + } + if len(describeImagesOutput.ImageDetails) >= expectedImages { + break + } + time.Sleep(5 * time.Second) + } + return describeImagesOutput, err +} + func getNodeJSBaseOptions(t *testing.T) integration.ProgramTestOptions { base := getBaseOptions(t) nodeBase := base.With(integration.ProgramTestOptions{ diff --git a/examples/ts-ecr-registry-image/Pulumi.yaml b/examples/ts-ecr-registry-image/Pulumi.yaml new file mode 100644 index 000000000..b9ee34921 --- /dev/null +++ b/examples/ts-ecr-registry-image/Pulumi.yaml @@ -0,0 +1,3 @@ +name: ecr-registry-image +runtime: nodejs +description: An example of pushing a local image to an ECR registry. diff --git a/examples/ts-ecr-registry-image/README.md b/examples/ts-ecr-registry-image/README.md new file mode 100644 index 000000000..ac60a9502 --- /dev/null +++ b/examples/ts-ecr-registry-image/README.md @@ -0,0 +1,5 @@ +# Pushing a local image to an ECR registry + +This example demonstrates how to push a local image to an ECR registry using the `awsx.ecr.RegistryImage` component. +The example builds a simple nginx image for demonstration purposes and pushes it to an ECR registry, demonstrating the +use of the `sourceImage` and `tag` properties. diff --git a/examples/ts-ecr-registry-image/app/Dockerfile b/examples/ts-ecr-registry-image/app/Dockerfile new file mode 100644 index 000000000..d8ce1aa42 --- /dev/null +++ b/examples/ts-ecr-registry-image/app/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx +ARG message +RUN echo "

${message}

" > \ + /usr/share/nginx/html/index.html diff --git a/examples/ts-ecr-registry-image/index.ts b/examples/ts-ecr-registry-image/index.ts new file mode 100644 index 000000000..581b14916 --- /dev/null +++ b/examples/ts-ecr-registry-image/index.ts @@ -0,0 +1,48 @@ +import * as pulumi from "@pulumi/pulumi"; +import * as awsx from "@pulumi/awsx"; +import * as docker from "@pulumi/docker"; + +const repository = new awsx.ecr.Repository("repository", { forceDelete: true }); +export const repositoryName = repository.repository.name; + +const config = new pulumi.Config(); +const message = config.require("message"); + +// build an image and keep it locally. You can replace this with any other local image you've already built or pulled. +const localImage = new docker.Image("local-image", { + build: { + context: "app", + args: { + message, + }, + }, + imageName: "my-awesome-image:test", + skipPush: true, +}); + +export const imageId = localImage.repoDigest; + +const latestImage = new awsx.ecr.RegistryImage("image-name", { + repositoryUrl: repository.url, + // if no tag is provided, the image will be pushed with the `latest` tag + sourceImage: localImage.imageName, + keepRemotely: true, +}); + +export const latestImageDigest = latestImage.image.sha256Digest; + +const taggedImage = new awsx.ecr.RegistryImage("tagged-image", { + repositoryUrl: repository.url, + sourceImage: localImage.imageName, + // if a tag is provided, it will be used for pushing to the registry + tag: "v1.0.0", + keepRemotely: true, +}); + +const digestImage = new awsx.ecr.RegistryImage("digest", { + repositoryUrl: repository.url, + // you can also specify a digest instead of an image name + sourceImage: localImage.repoDigest, + tag: "test", + keepRemotely: true, +}); diff --git a/examples/ts-ecr-registry-image/package.json b/examples/ts-ecr-registry-image/package.json new file mode 100644 index 000000000..d74cb6c2a --- /dev/null +++ b/examples/ts-ecr-registry-image/package.json @@ -0,0 +1,19 @@ +{ + "name": "ecr-registry-image", + "version": "0.0.1", + "license": "Apache-2.0", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@pulumi/pulumi": "^3.0.0", + "@pulumi/aws": "^6.0.0", + "@pulumi/docker": "^4.6.0" + }, + "devDependencies": { + "@types/node": "^18.0.0" + }, + "peerDependencies": { + "@pulumi/awsx": "latest" + } +} diff --git a/examples/ts-ecr-registry-image/tsconfig.json b/examples/ts-ecr-registry-image/tsconfig.json new file mode 100644 index 000000000..ab65afa61 --- /dev/null +++ b/examples/ts-ecr-registry-image/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "strict": true, + "outDir": "bin", + "target": "es2016", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.ts" + ] +} diff --git a/provider/cmd/pulumi-resource-awsx/schema.json b/provider/cmd/pulumi-resource-awsx/schema.json index 621a34bcf..4bfd75710 100644 --- a/provider/cmd/pulumi-resource-awsx/schema.json +++ b/provider/cmd/pulumi-resource-awsx/schema.json @@ -19,7 +19,8 @@ "packageReferences": { "Pulumi": "3.*", "Pulumi.Aws": "6.*", - "Pulumi.Docker": "4.*" + "Pulumi.Docker": "4.*", + "Pulumi.DockerBuild": "0.*" }, "respectSchemaVersion": true }, @@ -27,7 +28,8 @@ "generateResourceContainerTypes": true, "importBasePath": "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx", "internalDependencies": [ - "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild" + "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild", + "github.com/pulumi/pulumi-docker/sdk/v4/go/docker" ], "liftSingleValueMethodReturns": true, "respectSchemaVersion": true @@ -35,6 +37,7 @@ "java": { "dependencies": { "com.pulumi:aws": "6.66.2", + "com.pulumi:docker": "4.6.0", "com.pulumi:docker-build": "0.0.8" } }, @@ -42,6 +45,7 @@ "dependencies": { "@aws-sdk/client-ecs": "^3.405.0", "@pulumi/aws": "^6.66.2", + "@pulumi/docker": "^4.6.0", "@pulumi/docker-build": "^0.0.8", "@types/aws-lambda": "^8.10.23", "docker-classic": "npm:@pulumi/docker@3.6.1", @@ -62,6 +66,7 @@ "readme": "Pulumi Amazon Web Services (AWS) AWSX Components.", "requires": { "pulumi-aws": "\u003e=6.0.4,\u003c7.0.0", + "pulumi-docker": "\u003e=4.6.0,\u003c5.0.0", "pulumi-docker-build": "\u003e=0.0.8,\u003c1.0.0" }, "respectSchemaVersion": true, @@ -2225,6 +2230,54 @@ ], "isComponent": true }, + "awsx:ecr:RegistryImage": { + "description": "Manages the lifecycle of a docker image in a registry. You can upload images to a registry (= `docker push`) and also delete them again. In contrast to [`awsx.ecr.Image`](/registry/packages/awsx/api-docs/ecr/image/), this resource does not require to build the image, but can be used to push an existing image to an ECR repository. The image will be pushed whenever the source image changes or is updated.\n\n{{% examples %}}\n## Example Usage\n{{% example %}}\n### Pushing an image to an ECR repository\n\n```typescript\nimport * as pulumi from \"@pulumi/pulumi\";\nimport * as awsx from \"@pulumi/awsx\";\n\nconst repository = new awsx.ecr.Repository(\"repository\", { forceDelete: true });\n\nconst preTaggedImage = new awsx.ecr.RegistryImage(\"registry-image\", {\n repositoryUrl: repository.url,\n sourceImage: \"my-awesome-image:v1.0.0\",\n});\n```\n```python\nimport pulumi\nimport pulumi_awsx as awsx\n\nrepository = awsx.ecr.Repository(\"repository\", force_delete=True)\n\nregistry_image = awsx.ecr.RegistryImage(\"registry_image\",\n repository_url=repository.url,\n source_image=\"my-awesome-image:v1.0.0\")\n```\n```go\npackage main\n\nimport (\n\t\"github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ecr\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\trepository, err := ecr.NewRepository(ctx, \"repository\", \u0026ecr.RepositoryArgs{\n\t\t\tForceDelete: pulumi.Bool(true),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tregistryImage, err := ecr.NewRegistryImage(ctx, \"registryImage\", \u0026ecr.RegistryImageArgs{\n\t\t\tRepositoryUrl: repository.Url,\n\t\t\tSourceImage: pulumi.String(\"my-awesome-image:v1.0.0\"),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn nil\n\t})\n}\n```\n```csharp\nusing Pulumi;\nusing Pulumi.Awsx.Ecr;\n\nreturn await Pulumi.Deployment.RunAsync(() =\u003e\n{\n var repository = new Repository(\"repository\", new RepositoryArgs\n {\n ForceDelete = true,\n });\n\n var registryImage = new RegistryImage(\"registryImage\", new RegistryImageArgs\n {\n RepositoryUrl = repository.Url,\n SourceImage = \"my-awesome-image:v1.0.0\",\n });\n\n return new Dictionary\u003cstring, object?\u003e{};\n});\n```\n```yaml\nname: example\nruntime: yaml\nresources:\n repository:\n type: awsx:ecr:Repository\n properties:\n forceDelete: true\n registryImage:\n type: awsx:ecr:RegistryImage\n properties:\n repositoryUrl: ${repository.url}\n sourceImage: \"my-awesome-image:v1.0.0\"\n```\n```java\nimport com.pulumi.Pulumi;\nimport com.pulumi.awsx.ecr.Repository;\nimport com.pulumi.awsx.ecr.RepositoryArgs;\nimport com.pulumi.awsx.ecr.RegistryImage;\nimport com.pulumi.awsx.ecr.RegistryImageArgs;\n\npublic class Main {\n public static void main(String[] args) {\n Pulumi.run(ctx -\u003e {\n // Create an ECR repository with force delete enabled\n var repository = new Repository(\"repository\", RepositoryArgs.builder()\n .forceDelete(true)\n .build());\n\n // Create a RegistryImage based on the ECR repository URL and source image\n var registryImage = new RegistryImage(\"registryImage\", RegistryImageArgs.builder()\n .repositoryUrl(repository.url())\n .sourceImage(\"my-awesome-image:v1.0.0\")\n .build());\n });\n }\n}\n```\n{{% /example %}}\n{{% /examples %}}\n", + "properties": { + "image": { + "$ref": "/docker/v4.6.0/schema.json#/resources/docker:index%2fregistryImage:RegistryImage", + "description": "The underlying RegistryImage resource." + } + }, + "type": "object", + "required": [ + "image" + ], + "inputProperties": { + "insecureSkipVerify": { + "type": "boolean", + "description": "If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false`\n" + }, + "keepRemotely": { + "type": "boolean", + "description": "If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false`\n" + }, + "repositoryUrl": { + "type": "string", + "description": "The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName)." + }, + "sourceImage": { + "type": "string", + "description": "The source image to push to the registry." + }, + "tag": { + "type": "string", + "description": "The tag to use for the pushed image. If not provided, it defaults to `latest`." + }, + "triggers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image\n", + "willReplaceOnChanges": true + } + }, + "requiredInputs": [ + "repositoryUrl", + "sourceImage" + ], + "isComponent": true + }, "awsx:ecr:Repository": { "description": "A [Repository] represents an [aws.ecr.Repository] along with an associated [LifecyclePolicy] controlling how images are retained in the repo. \n\nDocker images can be built and pushed to the repo using the [buildAndPushImage] method. This will call into the `@pulumi/docker/buildAndPushImage` function using this repo as the appropriate destination registry.", "properties": { diff --git a/provider/pkg/schemagen/docs/ecr/registry-image.md b/provider/pkg/schemagen/docs/ecr/registry-image.md new file mode 100644 index 000000000..c1819c2ea --- /dev/null +++ b/provider/pkg/schemagen/docs/ecr/registry-image.md @@ -0,0 +1,117 @@ +Manages the lifecycle of a docker image in a registry. You can upload images to a registry (= `docker push`) and also delete them again. In contrast to [`awsx.ecr.Image`](/registry/packages/awsx/api-docs/ecr/image/), this resource does not require to build the image, but can be used to push an existing image to an ECR repository. The image will be pushed whenever the source image changes or is updated. + +{{% examples %}} +## Example Usage +{{% example %}} +### Pushing an image to an ECR repository + +```typescript +import * as pulumi from "@pulumi/pulumi"; +import * as awsx from "@pulumi/awsx"; + +const repository = new awsx.ecr.Repository("repository", { forceDelete: true }); + +const preTaggedImage = new awsx.ecr.RegistryImage("registry-image", { + repositoryUrl: repository.url, + sourceImage: "my-awesome-image:v1.0.0", +}); +``` +```python +import pulumi +import pulumi_awsx as awsx + +repository = awsx.ecr.Repository("repository", force_delete=True) + +registry_image = awsx.ecr.RegistryImage("registry_image", + repository_url=repository.url, + source_image="my-awesome-image:v1.0.0") +``` +```go +package main + +import ( + "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ecr" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + repository, err := ecr.NewRepository(ctx, "repository", &ecr.RepositoryArgs{ + ForceDelete: pulumi.Bool(true), + }) + if err != nil { + return err + } + + registryImage, err := ecr.NewRegistryImage(ctx, "registryImage", &ecr.RegistryImageArgs{ + RepositoryUrl: repository.Url, + SourceImage: pulumi.String("my-awesome-image:v1.0.0"), + }) + if err != nil { + return err + } + + return nil + }) +} +``` +```csharp +using Pulumi; +using Pulumi.Awsx.Ecr; + +return await Pulumi.Deployment.RunAsync(() => +{ + var repository = new Repository("repository", new RepositoryArgs + { + ForceDelete = true, + }); + + var registryImage = new RegistryImage("registryImage", new RegistryImageArgs + { + RepositoryUrl = repository.Url, + SourceImage = "my-awesome-image:v1.0.0", + }); + + return new Dictionary{}; +}); +``` +```yaml +name: example +runtime: yaml +resources: + repository: + type: awsx:ecr:Repository + properties: + forceDelete: true + registryImage: + type: awsx:ecr:RegistryImage + properties: + repositoryUrl: ${repository.url} + sourceImage: "my-awesome-image:v1.0.0" +``` +```java +import com.pulumi.Pulumi; +import com.pulumi.awsx.ecr.Repository; +import com.pulumi.awsx.ecr.RepositoryArgs; +import com.pulumi.awsx.ecr.RegistryImage; +import com.pulumi.awsx.ecr.RegistryImageArgs; + +public class Main { + public static void main(String[] args) { + Pulumi.run(ctx -> { + // Create an ECR repository with force delete enabled + var repository = new Repository("repository", RepositoryArgs.builder() + .forceDelete(true) + .build()); + + // Create a RegistryImage based on the ECR repository URL and source image + var registryImage = new RegistryImage("registryImage", RegistryImageArgs.builder() + .repositoryUrl(repository.url()) + .sourceImage("my-awesome-image:v1.0.0") + .build()); + }); + } +} +``` +{{% /example %}} +{{% /examples %}} diff --git a/provider/pkg/schemagen/ecr.go b/provider/pkg/schemagen/ecr.go index 09379fc22..aafa4514f 100644 --- a/provider/pkg/schemagen/ecr.go +++ b/provider/pkg/schemagen/ecr.go @@ -15,21 +15,28 @@ package gen import ( + // used for embedding docs + _ "embed" + "github.com/pulumi/pulumi/pkg/v3/codegen/schema" ) +//go:embed docs/ecr/registry-image.md +var registryImageDocs string + func generateEcr(awsSpec, dockerSpec schema.PackageSpec) schema.PackageSpec { return schema.PackageSpec{ Resources: map[string]schema.ResourceSpec{ - "awsx:ecr:Repository": repository(awsSpec), - "awsx:ecr:Image": ecrImage(awsSpec, dockerSpec), + "awsx:ecr:Repository": repository(awsSpec), + "awsx:ecr:Image": ecrImage(), + "awsx:ecr:RegistryImage": registryImage(dockerSpec), }, Types: map[string]schema.ComplexTypeSpec{ - "awsx:ecr:DockerBuild": dockerBuild(dockerSpec), + "awsx:ecr:DockerBuild": dockerBuild(), "awsx:ecr:BuilderVersion": builderVersion(), - "awsx:ecr:lifecyclePolicy": lifecyclePolicy(awsSpec), - "awsx:ecr:lifecyclePolicyRule": lifecyclePolicyRule(awsSpec), - "awsx:ecr:lifecycleTagStatus": lifecycleTagStatus(awsSpec), + "awsx:ecr:lifecyclePolicy": lifecyclePolicy(), + "awsx:ecr:lifecyclePolicyRule": lifecyclePolicyRule(), + "awsx:ecr:lifecycleTagStatus": lifecycleTagStatus(), }, } } @@ -90,7 +97,7 @@ func repository(awsSpec schema.PackageSpec) schema.ResourceSpec { } } -func lifecyclePolicy(_ schema.PackageSpec) schema.ComplexTypeSpec { +func lifecyclePolicy() schema.ComplexTypeSpec { return schema.ComplexTypeSpec{ ObjectTypeSpec: schema.ObjectTypeSpec{ Type: "object", @@ -123,7 +130,7 @@ func lifecyclePolicy(_ schema.PackageSpec) schema.ComplexTypeSpec { } } -func lifecyclePolicyRule(_ schema.PackageSpec) schema.ComplexTypeSpec { +func lifecyclePolicyRule() schema.ComplexTypeSpec { return schema.ComplexTypeSpec{ ObjectTypeSpec: schema.ObjectTypeSpec{ Type: "object", @@ -181,7 +188,7 @@ func lifecyclePolicyRule(_ schema.PackageSpec) schema.ComplexTypeSpec { } } -func lifecycleTagStatus(_ schema.PackageSpec) schema.ComplexTypeSpec { +func lifecycleTagStatus() schema.ComplexTypeSpec { return schema.ComplexTypeSpec{ ObjectTypeSpec: schema.ObjectTypeSpec{ Type: "string", @@ -206,8 +213,8 @@ func lifecycleTagStatus(_ schema.PackageSpec) schema.ComplexTypeSpec { } } -func ecrImage(_ schema.PackageSpec, dockerSpec schema.PackageSpec) schema.ResourceSpec { - inputs := dockerBuildProperties(dockerSpec) +func ecrImage() schema.ResourceSpec { + inputs := dockerBuildProperties() inputs["repositoryUrl"] = schema.PropertySpec{ Description: "Url of the repository", TypeSpec: schema.TypeSpec{ @@ -241,12 +248,12 @@ func ecrImage(_ schema.PackageSpec, dockerSpec schema.PackageSpec) schema.Resour } } -func dockerBuild(dockerSpec schema.PackageSpec) schema.ComplexTypeSpec { +func dockerBuild() schema.ComplexTypeSpec { return schema.ComplexTypeSpec{ ObjectTypeSpec: schema.ObjectTypeSpec{ Type: "object", Description: "Arguments for building a docker image", - Properties: dockerBuildProperties(dockerSpec), + Properties: dockerBuildProperties(), }, } } @@ -270,7 +277,7 @@ func builderVersion() schema.ComplexTypeSpec { } } -func dockerBuildProperties(_ schema.PackageSpec) map[string]schema.PropertySpec { +func dockerBuildProperties() map[string]schema.PropertySpec { return map[string]schema.PropertySpec{ "args": { Description: "An optional map of named build-time argument variables to " + @@ -347,3 +354,48 @@ func dockerBuildProperties(_ schema.PackageSpec) map[string]schema.PropertySpec }, } } + +func registryImage(dockerSpec schema.PackageSpec) schema.ResourceSpec { + originalSpec := dockerSpec.Resources["docker:index/registryImage:RegistryImage"] + inputProperties := renameDockerPropertiesRefs(dockerSpec, originalSpec.InputProperties) + inputProperties["repositoryUrl"] = schema.PropertySpec{ + Description: "The URL of the repository (in the form aws_account_id.dkr." + + "ecr.region.amazonaws.com/repositoryName).", + TypeSpec: schema.TypeSpec{ + Type: "string", + }, + } + + delete(inputProperties, "name") + inputProperties["sourceImage"] = schema.PropertySpec{ + Description: "The source image to push to the registry.", + TypeSpec: schema.TypeSpec{ + Type: "string", + }, + } + inputProperties["tag"] = schema.PropertySpec{ + Description: "The tag to use for the pushed image. If not provided, it defaults to `latest`.", + TypeSpec: schema.TypeSpec{ + Type: "string", + }, + } + + return schema.ResourceSpec{ + IsComponent: true, + InputProperties: inputProperties, + RequiredInputs: []string{"repositoryUrl", "sourceImage"}, + ObjectTypeSpec: schema.ObjectTypeSpec{ + Type: "object", + Description: registryImageDocs, + Properties: map[string]schema.PropertySpec{ + "image": { + Description: "The underlying RegistryImage resource.", + TypeSpec: schema.TypeSpec{ + Ref: packageRef(dockerSpec, "/resources/docker:index%2fregistryImage:RegistryImage"), + }, + }, + }, + Required: []string{"image"}, + }, + } +} diff --git a/provider/pkg/schemagen/schema.go b/provider/pkg/schemagen/schema.go index 3af92c27c..a3dd6c5cf 100644 --- a/provider/pkg/schemagen/schema.go +++ b/provider/pkg/schemagen/schema.go @@ -37,7 +37,7 @@ func GenerateSchema(packageDir string) schema.PackageSpec { dependencies := readPackageDependencies(packageDir) awsSpec := getPackageSpec("aws", dependencies.Aws) awsNativeSpec := getPackageSpec("aws-native", awsNativeTypesVersion) - dockerSpec := getPackageSpec("docker-build", dependencies.Docker) + dockerSpec := getPackageSpec("docker", dependencies.Docker) packageSpec := schema.PackageSpec{ Name: "awsx", @@ -56,9 +56,10 @@ func GenerateSchema(packageDir string) schema.PackageSpec { "csharp": rawMessage(map[string]interface{}{ "packageReferences": map[string]string{ // We use .* format rather than [x,y) because then it prefers the maximum satisfiable version - "Pulumi": "3.*", - "Pulumi.Aws": "6.*", - "Pulumi.Docker": "4.*", + "Pulumi": "3.*", + "Pulumi.Aws": "6.*", + "Pulumi.Docker": "4.*", + "Pulumi.DockerBuild": "0.*", }, "liftSingleValueMethodReturns": true, "respectSchemaVersion": true, @@ -67,20 +68,25 @@ func GenerateSchema(packageDir string) schema.PackageSpec { "generateResourceContainerTypes": true, "importBasePath": "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx", "liftSingleValueMethodReturns": true, - "internalDependencies": []string{"github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild"}, - "respectSchemaVersion": true, + "internalDependencies": []string{ + "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild", + "github.com/pulumi/pulumi-docker/sdk/v4/go/docker", + }, + "respectSchemaVersion": true, }), "java": rawMessage(map[string]interface{}{ "dependencies": map[string]string{ "com.pulumi:aws": dependencies.Aws, - "com.pulumi:docker-build": dependencies.Docker, + "com.pulumi:docker": dependencies.Docker, + "com.pulumi:docker-build": dependencies.DockerBuild, }, }), "nodejs": rawMessage(map[string]interface{}{ "dependencies": map[string]string{ "@aws-sdk/client-ecs": "^3.405.0", "@pulumi/aws": "^" + dependencies.Aws, - "@pulumi/docker-build": "^" + dependencies.Docker, + "@pulumi/docker": "^" + dependencies.Docker, + "@pulumi/docker-build": "^" + dependencies.DockerBuild, "docker-classic": "npm:@pulumi/docker@3.6.1", "@types/aws-lambda": "^8.10.23", "mime": "^2.0.0", @@ -95,7 +101,8 @@ func GenerateSchema(packageDir string) schema.PackageSpec { "python": rawMessage(map[string]interface{}{ "requires": map[string]string{ "pulumi-aws": ">=6.0.4,<7.0.0", - "pulumi-docker-build": fmt.Sprintf(">=%s,<1.0.0", dependencies.Docker), + "pulumi-docker": fmt.Sprintf(">=%s,<5.0.0", dependencies.Docker), + "pulumi-docker-build": fmt.Sprintf(">=%s,<1.0.0", dependencies.DockerBuild), }, "usesIOClasses": true, "readme": "Pulumi Amazon Web Services (AWS) AWSX Components.", @@ -170,6 +177,10 @@ func renameAwsPropertiesRefs(spec schema.PackageSpec, propertySpec map[string]sc return renamePropertiesRefs(propertySpec, "#/types/aws:", packageRef(spec, "/types/aws:")) } +func renameDockerPropertiesRefs(spec schema.PackageSpec, propertySpec map[string]schema.PropertySpec) map[string]schema.PropertySpec { + return renamePropertiesRefs(propertySpec, "#/types/docker:", packageRef(spec, "/types/docker:")) +} + func renamePropertiesRefs(propertySpec map[string]schema.PropertySpec, old, new string) map[string]schema.PropertySpec { properties := map[string]schema.PropertySpec{} for k, v := range propertySpec { @@ -252,9 +263,10 @@ func rawMessage(v interface{}) schema.RawMessage { } type Dependencies struct { - Aws string `json:"@pulumi/aws"` - Docker string `json:"@pulumi/docker-build"` - Pulumi string `json:"@pulumi/pulumi"` + Aws string `json:"@pulumi/aws"` + Docker string `json:"@pulumi/docker"` + DockerBuild string `json:"@pulumi/docker-build"` + Pulumi string `json:"@pulumi/pulumi"` } type PackageJson struct { diff --git a/sdk/dotnet/Ecr/RegistryImage.cs b/sdk/dotnet/Ecr/RegistryImage.cs new file mode 100644 index 000000000..227dc4f5e --- /dev/null +++ b/sdk/dotnet/Ecr/RegistryImage.cs @@ -0,0 +1,122 @@ +// *** WARNING: this file was generated by pulumi-gen-awsx. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Pulumi.Serialization; + +namespace Pulumi.Awsx.Ecr +{ + /// + /// Manages the lifecycle of a docker image in a registry. You can upload images to a registry (= `docker push`) and also delete them again. In contrast to [`awsx.ecr.Image`](/registry/packages/awsx/api-docs/ecr/image/), this resource does not require to build the image, but can be used to push an existing image to an ECR repository. The image will be pushed whenever the source image changes or is updated. + /// + /// ## Example Usage + /// ### Pushing an image to an ECR repository + /// ```csharp + /// using Pulumi; + /// using Pulumi.Awsx.Ecr; + /// + /// return await Pulumi.Deployment.RunAsync(() => + /// { + /// var repository = new Repository("repository", new RepositoryArgs + /// { + /// ForceDelete = true, + /// }); + /// + /// var registryImage = new RegistryImage("registryImage", new RegistryImageArgs + /// { + /// RepositoryUrl = repository.Url, + /// SourceImage = "my-awesome-image:v1.0.0", + /// }); + /// + /// return new Dictionary<string, object?>{}; + /// }); + /// ``` + /// + [AwsxResourceType("awsx:ecr:RegistryImage")] + public partial class RegistryImage : global::Pulumi.ComponentResource + { + /// + /// The underlying RegistryImage resource. + /// + [Output("image")] + public Output Image { get; private set; } = null!; + + + /// + /// Create a RegistryImage resource with the given unique name, arguments, and options. + /// + /// + /// The unique name of the resource + /// The arguments used to populate this resource's properties + /// A bag of options that control this resource's behavior + public RegistryImage(string name, RegistryImageArgs args, ComponentResourceOptions? options = null) + : base("awsx:ecr:RegistryImage", name, args ?? new RegistryImageArgs(), MakeResourceOptions(options, ""), remote: true) + { + } + + private static ComponentResourceOptions MakeResourceOptions(ComponentResourceOptions? options, Input? id) + { + var defaultOptions = new ComponentResourceOptions + { + Version = Utilities.Version, + }; + var merged = ComponentResourceOptions.Merge(defaultOptions, options); + // Override the ID if one was specified for consistency with other language SDKs. + merged.Id = id ?? merged.Id; + return merged; + } + } + + public sealed class RegistryImageArgs : global::Pulumi.ResourceArgs + { + /// + /// If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + /// + [Input("insecureSkipVerify")] + public Input? InsecureSkipVerify { get; set; } + + /// + /// If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + /// + [Input("keepRemotely")] + public Input? KeepRemotely { get; set; } + + /// + /// The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + /// + [Input("repositoryUrl", required: true)] + public Input RepositoryUrl { get; set; } = null!; + + /// + /// The source image to push to the registry. + /// + [Input("sourceImage", required: true)] + public Input SourceImage { get; set; } = null!; + + /// + /// The tag to use for the pushed image. If not provided, it defaults to `latest`. + /// + [Input("tag")] + public Input? Tag { get; set; } + + [Input("triggers")] + private InputMap? _triggers; + + /// + /// A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + /// + public InputMap Triggers + { + get => _triggers ?? (_triggers = new InputMap()); + set => _triggers = value; + } + + public RegistryImageArgs() + { + } + public static new RegistryImageArgs Empty => new RegistryImageArgs(); + } +} diff --git a/sdk/dotnet/Pulumi.Awsx.csproj b/sdk/dotnet/Pulumi.Awsx.csproj index 92a766b3a..173acca16 100644 --- a/sdk/dotnet/Pulumi.Awsx.csproj +++ b/sdk/dotnet/Pulumi.Awsx.csproj @@ -48,6 +48,7 @@ + diff --git a/sdk/go.mod b/sdk/go.mod index e23183843..7e884e460 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -8,6 +8,7 @@ require ( github.com/blang/semver v3.5.1+incompatible github.com/pulumi/pulumi-aws/sdk/v6 v6.66.2 github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild v0.0.3 + github.com/pulumi/pulumi-docker/sdk/v4 v4.5.8 github.com/pulumi/pulumi/sdk/v3 v3.144.1 ) diff --git a/sdk/go.sum b/sdk/go.sum index e494b6362..f6f3166d9 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -152,6 +152,8 @@ github.com/pulumi/pulumi-aws/sdk/v6 v6.66.2 h1:mHiPVwdSQp5VjpvCdPy52FeMiNcscKvJP github.com/pulumi/pulumi-aws/sdk/v6 v6.66.2/go.mod h1:C+L8LtajPSwGsZiGgknkCwClUzXk+ZXzSGkOZTQMp3U= github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild v0.0.3 h1:NxCXxRvzhUJP9dIvlpNlZKt/A3NHu3i9pC5XO+i8bR0= github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild v0.0.3/go.mod h1:jw9NcyRXjv5V2HHHJlqUBdXFCFiLfZoCChWEn38LR2A= +github.com/pulumi/pulumi-docker/sdk/v4 v4.5.8 h1:rik9L2SIpsoDenY51MkogR6GWgu/0Sy/XmyQmKWNUqU= +github.com/pulumi/pulumi-docker/sdk/v4 v4.5.8/go.mod h1:eph7BPNPkEIIK882/Ll4dbeHl5wZEc/UvTcUW0CK1UY= github.com/pulumi/pulumi/sdk/v3 v3.144.1 h1:QQtCDERihhlfvcmRzqHeBmOER1Fg1VkFj9933Lxqv00= github.com/pulumi/pulumi/sdk/v3 v3.144.1/go.mod h1:/6gxU2XirnLlImBy5OoqV6I4HcjOf+IznNIZNZExZzo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/sdk/go/awsx/cloudtrail/init.go b/sdk/go/awsx/cloudtrail/init.go index 693f137cd..011874518 100644 --- a/sdk/go/awsx/cloudtrail/init.go +++ b/sdk/go/awsx/cloudtrail/init.go @@ -9,6 +9,7 @@ import ( "github.com/blang/semver" "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/internal" _ "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild" + _ "github.com/pulumi/pulumi-docker/sdk/v4/go/docker" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) diff --git a/sdk/go/awsx/ec2/init.go b/sdk/go/awsx/ec2/init.go index 7236b0605..6c5cc654f 100644 --- a/sdk/go/awsx/ec2/init.go +++ b/sdk/go/awsx/ec2/init.go @@ -9,6 +9,7 @@ import ( "github.com/blang/semver" "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/internal" _ "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild" + _ "github.com/pulumi/pulumi-docker/sdk/v4/go/docker" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) diff --git a/sdk/go/awsx/ecr/init.go b/sdk/go/awsx/ecr/init.go index ee838f76a..ba5716673 100644 --- a/sdk/go/awsx/ecr/init.go +++ b/sdk/go/awsx/ecr/init.go @@ -9,6 +9,7 @@ import ( "github.com/blang/semver" "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/internal" _ "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild" + _ "github.com/pulumi/pulumi-docker/sdk/v4/go/docker" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) @@ -24,6 +25,8 @@ func (m *module) Construct(ctx *pulumi.Context, name, typ, urn string) (r pulumi switch typ { case "awsx:ecr:Image": r = &Image{} + case "awsx:ecr:RegistryImage": + r = &RegistryImage{} case "awsx:ecr:Repository": r = &Repository{} default: diff --git a/sdk/go/awsx/ecr/registryImage.go b/sdk/go/awsx/ecr/registryImage.go new file mode 100644 index 000000000..6a45dbb41 --- /dev/null +++ b/sdk/go/awsx/ecr/registryImage.go @@ -0,0 +1,251 @@ +// Code generated by pulumi-gen-awsx DO NOT EDIT. +// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! *** + +package ecr + +import ( + "context" + "reflect" + + "errors" + "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/internal" + "github.com/pulumi/pulumi-docker/sdk/v4/go/docker" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +// Manages the lifecycle of a docker image in a registry. You can upload images to a registry (= `docker push`) and also delete them again. In contrast to [`awsx.ecr.Image`](/registry/packages/awsx/api-docs/ecr/image/), this resource does not require to build the image, but can be used to push an existing image to an ECR repository. The image will be pushed whenever the source image changes or is updated. +// +// ## Example Usage +// ### Pushing an image to an ECR repository +// ```go +// package main +// +// import ( +// +// "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ecr" +// "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +// +// ) +// +// func main() { +// pulumi.Run(func(ctx *pulumi.Context) error { +// repository, err := ecr.NewRepository(ctx, "repository", &ecr.RepositoryArgs{ +// ForceDelete: pulumi.Bool(true), +// }) +// if err != nil { +// return err +// } +// +// registryImage, err := ecr.NewRegistryImage(ctx, "registryImage", &ecr.RegistryImageArgs{ +// RepositoryUrl: repository.Url, +// SourceImage: pulumi.String("my-awesome-image:v1.0.0"), +// }) +// if err != nil { +// return err +// } +// +// return nil +// }) +// } +// +// ``` +type RegistryImage struct { + pulumi.ResourceState + + // The underlying RegistryImage resource. + Image docker.RegistryImageOutput `pulumi:"image"` +} + +// NewRegistryImage registers a new resource with the given unique name, arguments, and options. +func NewRegistryImage(ctx *pulumi.Context, + name string, args *RegistryImageArgs, opts ...pulumi.ResourceOption) (*RegistryImage, error) { + if args == nil { + return nil, errors.New("missing one or more required arguments") + } + + if args.RepositoryUrl == nil { + return nil, errors.New("invalid value for required argument 'RepositoryUrl'") + } + if args.SourceImage == nil { + return nil, errors.New("invalid value for required argument 'SourceImage'") + } + opts = internal.PkgResourceDefaultOpts(opts) + var resource RegistryImage + err := ctx.RegisterRemoteComponentResource("awsx:ecr:RegistryImage", name, args, &resource, opts...) + if err != nil { + return nil, err + } + return &resource, nil +} + +type registryImageArgs struct { + // If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + InsecureSkipVerify *bool `pulumi:"insecureSkipVerify"` + // If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + KeepRemotely *bool `pulumi:"keepRemotely"` + // The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + RepositoryUrl string `pulumi:"repositoryUrl"` + // The source image to push to the registry. + SourceImage string `pulumi:"sourceImage"` + // The tag to use for the pushed image. If not provided, it defaults to `latest`. + Tag *string `pulumi:"tag"` + // A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + Triggers map[string]string `pulumi:"triggers"` +} + +// The set of arguments for constructing a RegistryImage resource. +type RegistryImageArgs struct { + // If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + InsecureSkipVerify pulumi.BoolPtrInput + // If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + KeepRemotely pulumi.BoolPtrInput + // The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + RepositoryUrl pulumi.StringInput + // The source image to push to the registry. + SourceImage pulumi.StringInput + // The tag to use for the pushed image. If not provided, it defaults to `latest`. + Tag pulumi.StringPtrInput + // A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + Triggers pulumi.StringMapInput +} + +func (RegistryImageArgs) ElementType() reflect.Type { + return reflect.TypeOf((*registryImageArgs)(nil)).Elem() +} + +type RegistryImageInput interface { + pulumi.Input + + ToRegistryImageOutput() RegistryImageOutput + ToRegistryImageOutputWithContext(ctx context.Context) RegistryImageOutput +} + +func (*RegistryImage) ElementType() reflect.Type { + return reflect.TypeOf((**RegistryImage)(nil)).Elem() +} + +func (i *RegistryImage) ToRegistryImageOutput() RegistryImageOutput { + return i.ToRegistryImageOutputWithContext(context.Background()) +} + +func (i *RegistryImage) ToRegistryImageOutputWithContext(ctx context.Context) RegistryImageOutput { + return pulumi.ToOutputWithContext(ctx, i).(RegistryImageOutput) +} + +// RegistryImageArrayInput is an input type that accepts RegistryImageArray and RegistryImageArrayOutput values. +// You can construct a concrete instance of `RegistryImageArrayInput` via: +// +// RegistryImageArray{ RegistryImageArgs{...} } +type RegistryImageArrayInput interface { + pulumi.Input + + ToRegistryImageArrayOutput() RegistryImageArrayOutput + ToRegistryImageArrayOutputWithContext(context.Context) RegistryImageArrayOutput +} + +type RegistryImageArray []RegistryImageInput + +func (RegistryImageArray) ElementType() reflect.Type { + return reflect.TypeOf((*[]*RegistryImage)(nil)).Elem() +} + +func (i RegistryImageArray) ToRegistryImageArrayOutput() RegistryImageArrayOutput { + return i.ToRegistryImageArrayOutputWithContext(context.Background()) +} + +func (i RegistryImageArray) ToRegistryImageArrayOutputWithContext(ctx context.Context) RegistryImageArrayOutput { + return pulumi.ToOutputWithContext(ctx, i).(RegistryImageArrayOutput) +} + +// RegistryImageMapInput is an input type that accepts RegistryImageMap and RegistryImageMapOutput values. +// You can construct a concrete instance of `RegistryImageMapInput` via: +// +// RegistryImageMap{ "key": RegistryImageArgs{...} } +type RegistryImageMapInput interface { + pulumi.Input + + ToRegistryImageMapOutput() RegistryImageMapOutput + ToRegistryImageMapOutputWithContext(context.Context) RegistryImageMapOutput +} + +type RegistryImageMap map[string]RegistryImageInput + +func (RegistryImageMap) ElementType() reflect.Type { + return reflect.TypeOf((*map[string]*RegistryImage)(nil)).Elem() +} + +func (i RegistryImageMap) ToRegistryImageMapOutput() RegistryImageMapOutput { + return i.ToRegistryImageMapOutputWithContext(context.Background()) +} + +func (i RegistryImageMap) ToRegistryImageMapOutputWithContext(ctx context.Context) RegistryImageMapOutput { + return pulumi.ToOutputWithContext(ctx, i).(RegistryImageMapOutput) +} + +type RegistryImageOutput struct{ *pulumi.OutputState } + +func (RegistryImageOutput) ElementType() reflect.Type { + return reflect.TypeOf((**RegistryImage)(nil)).Elem() +} + +func (o RegistryImageOutput) ToRegistryImageOutput() RegistryImageOutput { + return o +} + +func (o RegistryImageOutput) ToRegistryImageOutputWithContext(ctx context.Context) RegistryImageOutput { + return o +} + +// The underlying RegistryImage resource. +func (o RegistryImageOutput) Image() docker.RegistryImageOutput { + return o.ApplyT(func(v *RegistryImage) docker.RegistryImageOutput { return v.Image }).(docker.RegistryImageOutput) +} + +type RegistryImageArrayOutput struct{ *pulumi.OutputState } + +func (RegistryImageArrayOutput) ElementType() reflect.Type { + return reflect.TypeOf((*[]*RegistryImage)(nil)).Elem() +} + +func (o RegistryImageArrayOutput) ToRegistryImageArrayOutput() RegistryImageArrayOutput { + return o +} + +func (o RegistryImageArrayOutput) ToRegistryImageArrayOutputWithContext(ctx context.Context) RegistryImageArrayOutput { + return o +} + +func (o RegistryImageArrayOutput) Index(i pulumi.IntInput) RegistryImageOutput { + return pulumi.All(o, i).ApplyT(func(vs []interface{}) *RegistryImage { + return vs[0].([]*RegistryImage)[vs[1].(int)] + }).(RegistryImageOutput) +} + +type RegistryImageMapOutput struct{ *pulumi.OutputState } + +func (RegistryImageMapOutput) ElementType() reflect.Type { + return reflect.TypeOf((*map[string]*RegistryImage)(nil)).Elem() +} + +func (o RegistryImageMapOutput) ToRegistryImageMapOutput() RegistryImageMapOutput { + return o +} + +func (o RegistryImageMapOutput) ToRegistryImageMapOutputWithContext(ctx context.Context) RegistryImageMapOutput { + return o +} + +func (o RegistryImageMapOutput) MapIndex(k pulumi.StringInput) RegistryImageOutput { + return pulumi.All(o, k).ApplyT(func(vs []interface{}) *RegistryImage { + return vs[0].(map[string]*RegistryImage)[vs[1].(string)] + }).(RegistryImageOutput) +} + +func init() { + pulumi.RegisterInputType(reflect.TypeOf((*RegistryImageInput)(nil)).Elem(), &RegistryImage{}) + pulumi.RegisterInputType(reflect.TypeOf((*RegistryImageArrayInput)(nil)).Elem(), RegistryImageArray{}) + pulumi.RegisterInputType(reflect.TypeOf((*RegistryImageMapInput)(nil)).Elem(), RegistryImageMap{}) + pulumi.RegisterOutputType(RegistryImageOutput{}) + pulumi.RegisterOutputType(RegistryImageArrayOutput{}) + pulumi.RegisterOutputType(RegistryImageMapOutput{}) +} diff --git a/sdk/go/awsx/ecs/init.go b/sdk/go/awsx/ecs/init.go index 753dd55e0..9a0b3143e 100644 --- a/sdk/go/awsx/ecs/init.go +++ b/sdk/go/awsx/ecs/init.go @@ -9,6 +9,7 @@ import ( "github.com/blang/semver" "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/internal" _ "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild" + _ "github.com/pulumi/pulumi-docker/sdk/v4/go/docker" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) diff --git a/sdk/go/awsx/init.go b/sdk/go/awsx/init.go index 321628d3a..824ef462d 100644 --- a/sdk/go/awsx/init.go +++ b/sdk/go/awsx/init.go @@ -9,6 +9,7 @@ import ( "github.com/blang/semver" "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/internal" _ "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild" + _ "github.com/pulumi/pulumi-docker/sdk/v4/go/docker" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) diff --git a/sdk/go/awsx/lb/init.go b/sdk/go/awsx/lb/init.go index a33f45d18..8c8c46e8e 100644 --- a/sdk/go/awsx/lb/init.go +++ b/sdk/go/awsx/lb/init.go @@ -9,6 +9,7 @@ import ( "github.com/blang/semver" "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/internal" _ "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild" + _ "github.com/pulumi/pulumi-docker/sdk/v4/go/docker" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) diff --git a/sdk/java/build.gradle b/sdk/java/build.gradle index 657c7cab9..a371c9466 100644 --- a/sdk/java/build.gradle +++ b/sdk/java/build.gradle @@ -44,6 +44,7 @@ dependencies { implementation("com.google.code.findbugs:jsr305:3.0.2") implementation("com.google.code.gson:gson:2.8.9") implementation("com.pulumi:aws:6.66.2") + implementation("com.pulumi:docker:4.6.0") implementation("com.pulumi:docker-build:0.0.8") implementation("com.pulumi:pulumi:0.9.7") } diff --git a/sdk/java/src/main/java/com/pulumi/awsx/ecr/RegistryImage.java b/sdk/java/src/main/java/com/pulumi/awsx/ecr/RegistryImage.java new file mode 100644 index 000000000..d9b82a337 --- /dev/null +++ b/sdk/java/src/main/java/com/pulumi/awsx/ecr/RegistryImage.java @@ -0,0 +1,94 @@ +// *** WARNING: this file was generated by pulumi-java-gen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.awsx.ecr; + +import com.pulumi.awsx.Utilities; +import com.pulumi.awsx.ecr.RegistryImageArgs; +import com.pulumi.core.Output; +import com.pulumi.core.annotations.Export; +import com.pulumi.core.annotations.ResourceType; +import com.pulumi.core.internal.Codegen; +import javax.annotation.Nullable; + +/** + * Manages the lifecycle of a docker image in a registry. You can upload images to a registry (= `docker push`) and also delete them again. In contrast to [`awsx.ecr.Image`](/registry/packages/awsx/api-docs/ecr/image/), this resource does not require to build the image, but can be used to push an existing image to an ECR repository. The image will be pushed whenever the source image changes or is updated. + * + * ## Example Usage + * ### Pushing an image to an ECR repository + * ```java + * import com.pulumi.Pulumi; + * import com.pulumi.awsx.ecr.Repository; + * import com.pulumi.awsx.ecr.RepositoryArgs; + * import com.pulumi.awsx.ecr.RegistryImage; + * import com.pulumi.awsx.ecr.RegistryImageArgs; + * + * public class Main { + * public static void main(String[] args) { + * Pulumi.run(ctx -> { + * // Create an ECR repository with force delete enabled + * var repository = new Repository("repository", RepositoryArgs.builder() + * .forceDelete(true) + * .build()); + * + * // Create a RegistryImage based on the ECR repository URL and source image + * var registryImage = new RegistryImage("registryImage", RegistryImageArgs.builder() + * .repositoryUrl(repository.url()) + * .sourceImage("my-awesome-image:v1.0.0") + * .build()); + * }); + * } + * } + * ``` + * + */ +@ResourceType(type="awsx:ecr:RegistryImage") +public class RegistryImage extends com.pulumi.resources.ComponentResource { + /** + * The underlying RegistryImage resource. + * + */ + @Export(name="image", refs={com.pulumi.docker.RegistryImage.class}, tree="[0]") + private Output image; + + /** + * @return The underlying RegistryImage resource. + * + */ + public Output image() { + return this.image; + } + + /** + * + * @param name The _unique_ name of the resulting resource. + */ + public RegistryImage(String name) { + this(name, RegistryImageArgs.Empty); + } + /** + * + * @param name The _unique_ name of the resulting resource. + * @param args The arguments to use to populate this resource's properties. + */ + public RegistryImage(String name, RegistryImageArgs args) { + this(name, args, null); + } + /** + * + * @param name The _unique_ name of the resulting resource. + * @param args The arguments to use to populate this resource's properties. + * @param options A bag of options that control this resource's behavior. + */ + public RegistryImage(String name, RegistryImageArgs args, @Nullable com.pulumi.resources.ComponentResourceOptions options) { + super("awsx:ecr:RegistryImage", name, args == null ? RegistryImageArgs.Empty : args, makeResourceOptions(options, Codegen.empty()), true); + } + + private static com.pulumi.resources.ComponentResourceOptions makeResourceOptions(@Nullable com.pulumi.resources.ComponentResourceOptions options, @Nullable Output id) { + var defaultOptions = com.pulumi.resources.ComponentResourceOptions.builder() + .version(Utilities.getVersion()) + .build(); + return com.pulumi.resources.ComponentResourceOptions.merge(defaultOptions, options, id); + } + +} diff --git a/sdk/java/src/main/java/com/pulumi/awsx/ecr/RegistryImageArgs.java b/sdk/java/src/main/java/com/pulumi/awsx/ecr/RegistryImageArgs.java new file mode 100644 index 000000000..faaf11a5c --- /dev/null +++ b/sdk/java/src/main/java/com/pulumi/awsx/ecr/RegistryImageArgs.java @@ -0,0 +1,272 @@ +// *** WARNING: this file was generated by pulumi-java-gen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.awsx.ecr; + +import com.pulumi.core.Output; +import com.pulumi.core.annotations.Import; +import java.lang.Boolean; +import java.lang.String; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import javax.annotation.Nullable; + + +public final class RegistryImageArgs extends com.pulumi.resources.ResourceArgs { + + public static final RegistryImageArgs Empty = new RegistryImageArgs(); + + /** + * If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + * + */ + @Import(name="insecureSkipVerify") + private @Nullable Output insecureSkipVerify; + + /** + * @return If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + * + */ + public Optional> insecureSkipVerify() { + return Optional.ofNullable(this.insecureSkipVerify); + } + + /** + * If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + * + */ + @Import(name="keepRemotely") + private @Nullable Output keepRemotely; + + /** + * @return If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + * + */ + public Optional> keepRemotely() { + return Optional.ofNullable(this.keepRemotely); + } + + /** + * The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + * + */ + @Import(name="repositoryUrl", required=true) + private Output repositoryUrl; + + /** + * @return The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + * + */ + public Output repositoryUrl() { + return this.repositoryUrl; + } + + /** + * The source image to push to the registry. + * + */ + @Import(name="sourceImage", required=true) + private Output sourceImage; + + /** + * @return The source image to push to the registry. + * + */ + public Output sourceImage() { + return this.sourceImage; + } + + /** + * The tag to use for the pushed image. If not provided, it defaults to `latest`. + * + */ + @Import(name="tag") + private @Nullable Output tag; + + /** + * @return The tag to use for the pushed image. If not provided, it defaults to `latest`. + * + */ + public Optional> tag() { + return Optional.ofNullable(this.tag); + } + + /** + * A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + * + */ + @Import(name="triggers") + private @Nullable Output> triggers; + + /** + * @return A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + * + */ + public Optional>> triggers() { + return Optional.ofNullable(this.triggers); + } + + private RegistryImageArgs() {} + + private RegistryImageArgs(RegistryImageArgs $) { + this.insecureSkipVerify = $.insecureSkipVerify; + this.keepRemotely = $.keepRemotely; + this.repositoryUrl = $.repositoryUrl; + this.sourceImage = $.sourceImage; + this.tag = $.tag; + this.triggers = $.triggers; + } + + public static Builder builder() { + return new Builder(); + } + public static Builder builder(RegistryImageArgs defaults) { + return new Builder(defaults); + } + + public static final class Builder { + private RegistryImageArgs $; + + public Builder() { + $ = new RegistryImageArgs(); + } + + public Builder(RegistryImageArgs defaults) { + $ = new RegistryImageArgs(Objects.requireNonNull(defaults)); + } + + /** + * @param insecureSkipVerify If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + * + * @return builder + * + */ + public Builder insecureSkipVerify(@Nullable Output insecureSkipVerify) { + $.insecureSkipVerify = insecureSkipVerify; + return this; + } + + /** + * @param insecureSkipVerify If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + * + * @return builder + * + */ + public Builder insecureSkipVerify(Boolean insecureSkipVerify) { + return insecureSkipVerify(Output.of(insecureSkipVerify)); + } + + /** + * @param keepRemotely If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + * + * @return builder + * + */ + public Builder keepRemotely(@Nullable Output keepRemotely) { + $.keepRemotely = keepRemotely; + return this; + } + + /** + * @param keepRemotely If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + * + * @return builder + * + */ + public Builder keepRemotely(Boolean keepRemotely) { + return keepRemotely(Output.of(keepRemotely)); + } + + /** + * @param repositoryUrl The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + * + * @return builder + * + */ + public Builder repositoryUrl(Output repositoryUrl) { + $.repositoryUrl = repositoryUrl; + return this; + } + + /** + * @param repositoryUrl The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + * + * @return builder + * + */ + public Builder repositoryUrl(String repositoryUrl) { + return repositoryUrl(Output.of(repositoryUrl)); + } + + /** + * @param sourceImage The source image to push to the registry. + * + * @return builder + * + */ + public Builder sourceImage(Output sourceImage) { + $.sourceImage = sourceImage; + return this; + } + + /** + * @param sourceImage The source image to push to the registry. + * + * @return builder + * + */ + public Builder sourceImage(String sourceImage) { + return sourceImage(Output.of(sourceImage)); + } + + /** + * @param tag The tag to use for the pushed image. If not provided, it defaults to `latest`. + * + * @return builder + * + */ + public Builder tag(@Nullable Output tag) { + $.tag = tag; + return this; + } + + /** + * @param tag The tag to use for the pushed image. If not provided, it defaults to `latest`. + * + * @return builder + * + */ + public Builder tag(String tag) { + return tag(Output.of(tag)); + } + + /** + * @param triggers A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + * + * @return builder + * + */ + public Builder triggers(@Nullable Output> triggers) { + $.triggers = triggers; + return this; + } + + /** + * @param triggers A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + * + * @return builder + * + */ + public Builder triggers(Map triggers) { + return triggers(Output.of(triggers)); + } + + public RegistryImageArgs build() { + $.repositoryUrl = Objects.requireNonNull($.repositoryUrl, "expected parameter 'repositoryUrl' to be non-null"); + $.sourceImage = Objects.requireNonNull($.sourceImage, "expected parameter 'sourceImage' to be non-null"); + return $; + } + } + +} diff --git a/sdk/nodejs/ecr/index.ts b/sdk/nodejs/ecr/index.ts index 6e2559a82..ed30bdf02 100644 --- a/sdk/nodejs/ecr/index.ts +++ b/sdk/nodejs/ecr/index.ts @@ -10,6 +10,11 @@ export type Image = import("./image").Image; export const Image: typeof import("./image").Image = null as any; utilities.lazyLoad(exports, ["Image"], () => require("./image")); +export { RegistryImageArgs } from "./registryImage"; +export type RegistryImage = import("./registryImage").RegistryImage; +export const RegistryImage: typeof import("./registryImage").RegistryImage = null as any; +utilities.lazyLoad(exports, ["RegistryImage"], () => require("./registryImage")); + export { RepositoryArgs } from "./repository"; export type Repository = import("./repository").Repository; export const Repository: typeof import("./repository").Repository = null as any; @@ -25,6 +30,8 @@ const _module = { switch (type) { case "awsx:ecr:Image": return new Image(name, undefined, { urn }) + case "awsx:ecr:RegistryImage": + return new RegistryImage(name, undefined, { urn }) case "awsx:ecr:Repository": return new Repository(name, undefined, { urn }) default: diff --git a/sdk/nodejs/ecr/registryImage.ts b/sdk/nodejs/ecr/registryImage.ts new file mode 100644 index 000000000..d7bc9cfd2 --- /dev/null +++ b/sdk/nodejs/ecr/registryImage.ts @@ -0,0 +1,107 @@ +// *** WARNING: this file was generated by pulumi-gen-awsx. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +import * as pulumi from "@pulumi/pulumi"; +import * as utilities from "../utilities"; + +import * as pulumiDocker from "@pulumi/docker"; + +/** + * Manages the lifecycle of a docker image in a registry. You can upload images to a registry (= `docker push`) and also delete them again. In contrast to [`awsx.ecr.Image`](/registry/packages/awsx/api-docs/ecr/image/), this resource does not require to build the image, but can be used to push an existing image to an ECR repository. The image will be pushed whenever the source image changes or is updated. + * + * ## Example Usage + * ### Pushing an image to an ECR repository + * + * ```typescript + * import * as pulumi from "@pulumi/pulumi"; + * import * as awsx from "@pulumi/awsx"; + * + * const repository = new awsx.ecr.Repository("repository", { forceDelete: true }); + * + * const preTaggedImage = new awsx.ecr.RegistryImage("registry-image", { + * repositoryUrl: repository.url, + * sourceImage: "my-awesome-image:v1.0.0", + * }); + * ``` + */ +export class RegistryImage extends pulumi.ComponentResource { + /** @internal */ + public static readonly __pulumiType = 'awsx:ecr:RegistryImage'; + + /** + * Returns true if the given object is an instance of RegistryImage. This is designed to work even + * when multiple copies of the Pulumi SDK have been loaded into the same process. + */ + public static isInstance(obj: any): obj is RegistryImage { + if (obj === undefined || obj === null) { + return false; + } + return obj['__pulumiType'] === RegistryImage.__pulumiType; + } + + /** + * The underlying RegistryImage resource. + */ + public /*out*/ readonly image!: pulumi.Output; + + /** + * Create a RegistryImage resource with the given unique name, arguments, and options. + * + * @param name The _unique_ name of the resource. + * @param args The arguments to use to populate this resource's properties. + * @param opts A bag of options that control this resource's behavior. + */ + constructor(name: string, args: RegistryImageArgs, opts?: pulumi.ComponentResourceOptions) { + let resourceInputs: pulumi.Inputs = {}; + opts = opts || {}; + if (!opts.id) { + if ((!args || args.repositoryUrl === undefined) && !opts.urn) { + throw new Error("Missing required property 'repositoryUrl'"); + } + if ((!args || args.sourceImage === undefined) && !opts.urn) { + throw new Error("Missing required property 'sourceImage'"); + } + resourceInputs["insecureSkipVerify"] = args ? args.insecureSkipVerify : undefined; + resourceInputs["keepRemotely"] = args ? args.keepRemotely : undefined; + resourceInputs["repositoryUrl"] = args ? args.repositoryUrl : undefined; + resourceInputs["sourceImage"] = args ? args.sourceImage : undefined; + resourceInputs["tag"] = args ? args.tag : undefined; + resourceInputs["triggers"] = args ? args.triggers : undefined; + resourceInputs["image"] = undefined /*out*/; + } else { + resourceInputs["image"] = undefined /*out*/; + } + opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts); + super(RegistryImage.__pulumiType, name, resourceInputs, opts, true /*remote*/); + } +} + +/** + * The set of arguments for constructing a RegistryImage resource. + */ +export interface RegistryImageArgs { + /** + * If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + */ + insecureSkipVerify?: pulumi.Input; + /** + * If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + */ + keepRemotely?: pulumi.Input; + /** + * The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + */ + repositoryUrl: pulumi.Input; + /** + * The source image to push to the registry. + */ + sourceImage: pulumi.Input; + /** + * The tag to use for the pushed image. If not provided, it defaults to `latest`. + */ + tag?: pulumi.Input; + /** + * A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + */ + triggers?: pulumi.Input<{[key: string]: pulumi.Input}>; +} diff --git a/sdk/nodejs/package.json b/sdk/nodejs/package.json index cf1e6dbc1..1ea10ae52 100644 --- a/sdk/nodejs/package.json +++ b/sdk/nodejs/package.json @@ -17,6 +17,7 @@ "dependencies": { "@aws-sdk/client-ecs": "^3.405.0", "@pulumi/aws": "^6.66.2", + "@pulumi/docker": "^4.6.0", "@pulumi/docker-build": "^0.0.8", "@pulumi/pulumi": "^3.142.0", "@types/aws-lambda": "^8.10.23", diff --git a/sdk/nodejs/tsconfig.json b/sdk/nodejs/tsconfig.json index 213e9bce6..f56c903a2 100644 --- a/sdk/nodejs/tsconfig.json +++ b/sdk/nodejs/tsconfig.json @@ -111,6 +111,7 @@ "ec2/vpc.ts", "ecr/image.ts", "ecr/index.ts", + "ecr/registryImage.ts", "ecr/repository.ts", "ecs/ec2service.ts", "ecs/ec2taskDefinition.ts", diff --git a/sdk/python/pulumi_awsx/__init__.py b/sdk/python/pulumi_awsx/__init__.py index 6032a297f..5e177c092 100644 --- a/sdk/python/pulumi_awsx/__init__.py +++ b/sdk/python/pulumi_awsx/__init__.py @@ -55,6 +55,7 @@ "fqn": "pulumi_awsx.ecr", "classes": { "awsx:ecr:Image": "Image", + "awsx:ecr:RegistryImage": "RegistryImage", "awsx:ecr:Repository": "Repository" } }, diff --git a/sdk/python/pulumi_awsx/ecr/__init__.py b/sdk/python/pulumi_awsx/ecr/__init__.py index 9137853aa..38a3d52e4 100644 --- a/sdk/python/pulumi_awsx/ecr/__init__.py +++ b/sdk/python/pulumi_awsx/ecr/__init__.py @@ -7,5 +7,6 @@ # Export this package's modules as members: from ._enums import * from .image import * +from .registry_image import * from .repository import * from ._inputs import * diff --git a/sdk/python/pulumi_awsx/ecr/registry_image.py b/sdk/python/pulumi_awsx/ecr/registry_image.py new file mode 100644 index 000000000..b7cf43679 --- /dev/null +++ b/sdk/python/pulumi_awsx/ecr/registry_image.py @@ -0,0 +1,238 @@ +# coding=utf-8 +# *** WARNING: this file was generated by pulumi-gen-awsx. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +import copy +import warnings +import sys +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +if sys.version_info >= (3, 11): + from typing import NotRequired, TypedDict, TypeAlias +else: + from typing_extensions import NotRequired, TypedDict, TypeAlias +from .. import _utilities +import pulumi_docker + +__all__ = ['RegistryImageArgs', 'RegistryImage'] + +@pulumi.input_type +class RegistryImageArgs: + def __init__(__self__, *, + repository_url: pulumi.Input[str], + source_image: pulumi.Input[str], + insecure_skip_verify: Optional[pulumi.Input[bool]] = None, + keep_remotely: Optional[pulumi.Input[bool]] = None, + tag: Optional[pulumi.Input[str]] = None, + triggers: Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]] = None): + """ + The set of arguments for constructing a RegistryImage resource. + :param pulumi.Input[str] repository_url: The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + :param pulumi.Input[str] source_image: The source image to push to the registry. + :param pulumi.Input[bool] insecure_skip_verify: If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + :param pulumi.Input[bool] keep_remotely: If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + :param pulumi.Input[str] tag: The tag to use for the pushed image. If not provided, it defaults to `latest`. + :param pulumi.Input[Mapping[str, pulumi.Input[str]]] triggers: A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + """ + pulumi.set(__self__, "repository_url", repository_url) + pulumi.set(__self__, "source_image", source_image) + if insecure_skip_verify is not None: + pulumi.set(__self__, "insecure_skip_verify", insecure_skip_verify) + if keep_remotely is not None: + pulumi.set(__self__, "keep_remotely", keep_remotely) + if tag is not None: + pulumi.set(__self__, "tag", tag) + if triggers is not None: + pulumi.set(__self__, "triggers", triggers) + + @property + @pulumi.getter(name="repositoryUrl") + def repository_url(self) -> pulumi.Input[str]: + """ + The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + """ + return pulumi.get(self, "repository_url") + + @repository_url.setter + def repository_url(self, value: pulumi.Input[str]): + pulumi.set(self, "repository_url", value) + + @property + @pulumi.getter(name="sourceImage") + def source_image(self) -> pulumi.Input[str]: + """ + The source image to push to the registry. + """ + return pulumi.get(self, "source_image") + + @source_image.setter + def source_image(self, value: pulumi.Input[str]): + pulumi.set(self, "source_image", value) + + @property + @pulumi.getter(name="insecureSkipVerify") + def insecure_skip_verify(self) -> Optional[pulumi.Input[bool]]: + """ + If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + """ + return pulumi.get(self, "insecure_skip_verify") + + @insecure_skip_verify.setter + def insecure_skip_verify(self, value: Optional[pulumi.Input[bool]]): + pulumi.set(self, "insecure_skip_verify", value) + + @property + @pulumi.getter(name="keepRemotely") + def keep_remotely(self) -> Optional[pulumi.Input[bool]]: + """ + If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + """ + return pulumi.get(self, "keep_remotely") + + @keep_remotely.setter + def keep_remotely(self, value: Optional[pulumi.Input[bool]]): + pulumi.set(self, "keep_remotely", value) + + @property + @pulumi.getter + def tag(self) -> Optional[pulumi.Input[str]]: + """ + The tag to use for the pushed image. If not provided, it defaults to `latest`. + """ + return pulumi.get(self, "tag") + + @tag.setter + def tag(self, value: Optional[pulumi.Input[str]]): + pulumi.set(self, "tag", value) + + @property + @pulumi.getter + def triggers(self) -> Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]]: + """ + A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + """ + return pulumi.get(self, "triggers") + + @triggers.setter + def triggers(self, value: Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]]): + pulumi.set(self, "triggers", value) + + +class RegistryImage(pulumi.ComponentResource): + @overload + def __init__(__self__, + resource_name: str, + opts: Optional[pulumi.ResourceOptions] = None, + insecure_skip_verify: Optional[pulumi.Input[bool]] = None, + keep_remotely: Optional[pulumi.Input[bool]] = None, + repository_url: Optional[pulumi.Input[str]] = None, + source_image: Optional[pulumi.Input[str]] = None, + tag: Optional[pulumi.Input[str]] = None, + triggers: Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]] = None, + __props__=None): + """ + Manages the lifecycle of a docker image in a registry. You can upload images to a registry (= `docker push`) and also delete them again. In contrast to [`awsx.ecr.Image`](/registry/packages/awsx/api-docs/ecr/image/), this resource does not require to build the image, but can be used to push an existing image to an ECR repository. The image will be pushed whenever the source image changes or is updated. + + ## Example Usage + ### Pushing an image to an ECR repository + ```python + import pulumi + import pulumi_awsx as awsx + + repository = awsx.ecr.Repository("repository", force_delete=True) + + registry_image = awsx.ecr.RegistryImage("registry_image", + repository_url=repository.url, + source_image="my-awesome-image:v1.0.0") + ``` + + :param str resource_name: The name of the resource. + :param pulumi.ResourceOptions opts: Options for the resource. + :param pulumi.Input[bool] insecure_skip_verify: If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false` + :param pulumi.Input[bool] keep_remotely: If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false` + :param pulumi.Input[str] repository_url: The URL of the repository (in the form aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName). + :param pulumi.Input[str] source_image: The source image to push to the registry. + :param pulumi.Input[str] tag: The tag to use for the pushed image. If not provided, it defaults to `latest`. + :param pulumi.Input[Mapping[str, pulumi.Input[str]]] triggers: A map of arbitrary strings that, when changed, will force the `docker.RegistryImage` resource to be replaced. This can be used to repush a local image + """ + ... + @overload + def __init__(__self__, + resource_name: str, + args: RegistryImageArgs, + opts: Optional[pulumi.ResourceOptions] = None): + """ + Manages the lifecycle of a docker image in a registry. You can upload images to a registry (= `docker push`) and also delete them again. In contrast to [`awsx.ecr.Image`](/registry/packages/awsx/api-docs/ecr/image/), this resource does not require to build the image, but can be used to push an existing image to an ECR repository. The image will be pushed whenever the source image changes or is updated. + + ## Example Usage + ### Pushing an image to an ECR repository + ```python + import pulumi + import pulumi_awsx as awsx + + repository = awsx.ecr.Repository("repository", force_delete=True) + + registry_image = awsx.ecr.RegistryImage("registry_image", + repository_url=repository.url, + source_image="my-awesome-image:v1.0.0") + ``` + + :param str resource_name: The name of the resource. + :param RegistryImageArgs args: The arguments to use to populate this resource's properties. + :param pulumi.ResourceOptions opts: Options for the resource. + """ + ... + def __init__(__self__, resource_name: str, *args, **kwargs): + resource_args, opts = _utilities.get_resource_args_opts(RegistryImageArgs, pulumi.ResourceOptions, *args, **kwargs) + if resource_args is not None: + __self__._internal_init(resource_name, opts, **resource_args.__dict__) + else: + __self__._internal_init(resource_name, *args, **kwargs) + + def _internal_init(__self__, + resource_name: str, + opts: Optional[pulumi.ResourceOptions] = None, + insecure_skip_verify: Optional[pulumi.Input[bool]] = None, + keep_remotely: Optional[pulumi.Input[bool]] = None, + repository_url: Optional[pulumi.Input[str]] = None, + source_image: Optional[pulumi.Input[str]] = None, + tag: Optional[pulumi.Input[str]] = None, + triggers: Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]] = None, + __props__=None): + opts = pulumi.ResourceOptions.merge(_utilities.get_resource_opts_defaults(), opts) + if not isinstance(opts, pulumi.ResourceOptions): + raise TypeError('Expected resource options to be a ResourceOptions instance') + if opts.id is not None: + raise ValueError('ComponentResource classes do not support opts.id') + else: + if __props__ is not None: + raise TypeError('__props__ is only valid when passed in combination with a valid opts.id to get an existing resource') + __props__ = RegistryImageArgs.__new__(RegistryImageArgs) + + __props__.__dict__["insecure_skip_verify"] = insecure_skip_verify + __props__.__dict__["keep_remotely"] = keep_remotely + if repository_url is None and not opts.urn: + raise TypeError("Missing required property 'repository_url'") + __props__.__dict__["repository_url"] = repository_url + if source_image is None and not opts.urn: + raise TypeError("Missing required property 'source_image'") + __props__.__dict__["source_image"] = source_image + __props__.__dict__["tag"] = tag + __props__.__dict__["triggers"] = triggers + __props__.__dict__["image"] = None + super(RegistryImage, __self__).__init__( + 'awsx:ecr:RegistryImage', + resource_name, + __props__, + opts, + remote=True) + + @property + @pulumi.getter + def image(self) -> pulumi.Output['pulumi_docker.RegistryImage']: + """ + The underlying RegistryImage resource. + """ + return pulumi.get(self, "image") + diff --git a/sdk/python/pyproject.toml b/sdk/python/pyproject.toml index a9acb7bf5..dfa7d114f 100644 --- a/sdk/python/pyproject.toml +++ b/sdk/python/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "pulumi_awsx" description = "Pulumi Amazon Web Services (AWS) AWSX Components." - dependencies = ["parver>=0.2.1", "pulumi>=3.142.0,<4.0.0", "pulumi-aws>=6.0.4,<7.0.0", "pulumi-docker-build>=0.0.8,<1.0.0", "semver>=2.8.1", "typing-extensions>=4.11; python_version < \"3.11\""] + dependencies = ["parver>=0.2.1", "pulumi>=3.142.0,<4.0.0", "pulumi-aws>=6.0.4,<7.0.0", "pulumi-docker>=4.6.0,<5.0.0", "pulumi-docker-build>=0.0.8,<1.0.0", "semver>=2.8.1", "typing-extensions>=4.11; python_version < \"3.11\""] keywords = ["pulumi", "aws", "awsx", "kind/component", "category/cloud"] readme = "README.md" requires-python = ">=3.9"