├── .github └── workflows │ └── create-release.yml ├── .gitignore ├── ATTRIBUTION.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GOVERNANCE.md ├── LICENSE ├── Makefile ├── NOTICE ├── OWNERS ├── OWNERS_ALIASES ├── README.md ├── apis └── v1alpha1 │ ├── ack-generate-metadata.yaml │ ├── doc.go │ ├── enums.go │ ├── generator.yaml │ ├── groupversion_info.go │ ├── pull_through_cache_rule.go │ ├── repository.go │ ├── types.go │ └── zz_generated.deepcopy.go ├── cmd └── controller │ └── main.go ├── config ├── controller │ ├── deployment.yaml │ ├── kustomization.yaml │ └── service.yaml ├── crd │ ├── bases │ │ ├── ecr.services.k8s.aws_pullthroughcacherules.yaml │ │ └── ecr.services.k8s.aws_repositories.yaml │ ├── common │ │ ├── bases │ │ │ ├── services.k8s.aws_adoptedresources.yaml │ │ │ └── services.k8s.aws_fieldexports.yaml │ │ └── kustomization.yaml │ └── kustomization.yaml ├── default │ └── kustomization.yaml ├── iam │ └── recommended-policy-arn ├── overlays │ └── namespaced │ │ ├── kustomization.yaml │ │ ├── role-binding.json │ │ └── role.json └── rbac │ ├── cluster-role-binding.yaml │ ├── cluster-role-controller.yaml │ ├── kustomization.yaml │ ├── leader-election-role-binding.yaml │ ├── leader-election-role.yaml │ ├── role-reader.yaml │ ├── role-writer.yaml │ └── service-account.yaml ├── generator.yaml ├── go.local.mod ├── go.mod ├── go.sum ├── helm ├── Chart.yaml ├── crds │ ├── ecr.services.k8s.aws_pullthroughcacherules.yaml │ ├── ecr.services.k8s.aws_repositories.yaml │ ├── services.k8s.aws_adoptedresources.yaml │ └── services.k8s.aws_fieldexports.yaml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── caches-role-binding.yaml │ ├── caches-role.yaml │ ├── cluster-role-binding.yaml │ ├── cluster-role-controller.yaml │ ├── deployment.yaml │ ├── leader-election-role-binding.yaml │ ├── leader-election-role.yaml │ ├── metrics-service.yaml │ ├── role-reader.yaml │ ├── role-writer.yaml │ └── service-account.yaml ├── values.schema.json └── values.yaml ├── metadata.yaml ├── olm └── olmconfig.yaml ├── pkg ├── resource │ ├── pull_through_cache_rule │ │ ├── delta.go │ │ ├── descriptor.go │ │ ├── identifiers.go │ │ ├── manager.go │ │ ├── manager_factory.go │ │ ├── references.go │ │ ├── resource.go │ │ └── sdk.go │ ├── registry.go │ └── repository │ │ ├── custom_update_api.go │ │ ├── delta.go │ │ ├── descriptor.go │ │ ├── hook.go │ │ ├── hook_test.go │ │ ├── identifiers.go │ │ ├── manager.go │ │ ├── manager_factory.go │ │ ├── references.go │ │ ├── resource.go │ │ ├── sdk.go │ │ └── tags.go └── version │ └── version.go ├── templates └── hooks │ ├── pull_through_cache_rule │ └── sdk_read_many_post_build_request.go.tpl │ └── repository │ ├── sdk_create_post_set_output.go.tpl │ ├── sdk_delete_post_build_request.go.tpl │ └── sdk_read_many_post_set_output.go.tpl └── test └── e2e ├── .gitignore ├── __init__.py ├── bootstrap_resources.py ├── conftest.py ├── fixtures.py ├── replacement_values.py ├── requirements.txt ├── resources ├── pull_through_cache_rule.yaml ├── repository.yaml ├── repository_all_fields.yaml ├── repository_carm.yaml ├── repository_lifecycle_policy.yaml ├── repository_policy.yaml └── repository_region.yaml ├── service_bootstrap.py ├── service_cleanup.py └── tests ├── __init__.py ├── test_cross_account.py ├── test_cross_region.py ├── test_pull_through_cache_rule.py └── test_repository.py /.github/workflows/create-release.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | permissions: 9 | contents: write # For creating releases 10 | 11 | jobs: 12 | call-create-release: 13 | uses: aws-controllers-k8s/.github/.github/workflows/reusable-create-release.yaml@main 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | *~ 4 | .idea 5 | /docs/site 6 | bin 7 | build 8 | go.local.sum -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug 4 | report, new feature, correction, or additional documentation, we greatly value 5 | feedback and contributions from our community. 6 | 7 | Please read through this document before submitting any issues or pull requests 8 | to ensure we have all the necessary information to effectively respond to your 9 | bug report or contribution. 10 | 11 | ## Reporting Bugs/Feature Requests 12 | 13 | We welcome you to use the GitHub issue tracker to report bugs or suggest 14 | features. 15 | 16 | When filing an issue, please check existing open, or recently closed, issues to 17 | make sure somebody else hasn't already reported the issue. Please try to 18 | include as much information as you can. Details like these are incredibly 19 | useful: 20 | 21 | * A reproducible test case or series of steps 22 | * The version of our code being used 23 | * Any modifications you've made relevant to the bug 24 | * Anything unusual about your environment or deployment 25 | 26 | ## Contributing via Pull Requests 27 | 28 | Contributions via pull requests are much appreciated. Before sending us a pull 29 | request, please ensure that: 30 | 31 | 1. You are working against the latest source on the *main* branch. 32 | 2. You check existing open, and recently merged, pull requests to make sure 33 | someone else hasn't addressed the problem already. 34 | 3. You open an issue to discuss any significant work - we would hate for your 35 | time to be wasted. 36 | 37 | To send us a pull request, please: 38 | 39 | 1. Fork the repository. 40 | 2. Modify the source; please focus on the specific change you are contributing. 41 | If you also reformat all the code, it will be hard for us to focus on your 42 | change. 43 | 3. Ensure local tests pass. 44 | 4. Commit to your fork using clear commit messages. 45 | 5. Send us a pull request, answering any default questions in the pull request 46 | interface. 47 | 6. Pay attention to any automated CI failures reported in the pull request, and 48 | stay involved in the conversation. 49 | 50 | GitHub provides additional document on [forking a repository][fork] and 51 | [creating a pull request][pr]. 52 | 53 | [fork]: https://help.github.com/articles/fork-a-repo/ 54 | [pr]: https://help.github.com/articles/creating-a-pull-request/ 55 | 56 | ## Finding contributions to work on 57 | 58 | Looking at the existing issues is a great way to find something to contribute 59 | on. As our projects, by default, use the default GitHub issue labels 60 | (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at 61 | any 'help wanted' issues is a great place to start. 62 | 63 | ## Developer documentation 64 | 65 | [See the documentation][dev-docs] for detailed development information. 66 | 67 | [dev-docs]: https://aws-controllers-k8s.github.io/community/docs/contributor-docs/overview/ 68 | 69 | ## Code of Conduct 70 | 71 | We adhere to the [Amazon Open Source Code of Conduct][coc]. 72 | 73 | [coc]: https://aws.github.io/code-of-conduct 74 | 75 | ## Security issue notifications 76 | 77 | If you discover a potential security issue in this project we ask that you 78 | notify AWS/Amazon Security via our [vulnerability reporting page][vuln]. Please 79 | do **not** create a public Github issue. 80 | 81 | [vuln]: http://aws.amazon.com/security/vulnerability-reporting/ 82 | 83 | ## License 84 | 85 | This project is [licensed][./LICENSE] under the Apache-2.0 License. 86 | -------------------------------------------------------------------------------- /GOVERNANCE.md: -------------------------------------------------------------------------------- 1 | # Project governance 2 | 3 | This document lays out the guidelines under which the AWS Controllers for Kubernetes (ACK) project will be governed. 4 | The goal is to make sure that the roles and responsibilities are well defined and clarify on how decisions are made. 5 | 6 | ## Roles 7 | 8 | In the context of ACK, we consider the following roles: 9 | 10 | * __Users__ ... everyone using ACK, typically willing to provide feedback on ACK by proposing features and/or filing issues. 11 | * __Contributors__ ... everyone contributing code, documentation, examples, testing infra, and participating in feature proposals as well as design discussions. Code contributions will require a Developer Certificate of Origin (DCO). 12 | * __Maintainers__ ... are responsible for engaging with and assisting contributors to iterate on the contributions until it reaches acceptable quality. Maintainers can decide whether the contributions can be accepted into the project or rejected. Any active contributor meeting the project quality can be made a Maintainer by the Advisory Board. 13 | * __Advisory Board__ ... is responsible for defining the guidelines and processes that the project operates under. 14 | 15 | The initial members of the Advisory Board are `@jaypipes` and `@mhausenblas`. 16 | 17 | 18 | ## Communication 19 | 20 | The primary mechanism for communication will be via the `#provider-aws` channel on the Kubernetes Slack community. 21 | All features and bug fixes will be tracked as issues in GitHub. All decisions will be documented in GitHub issues. 22 | 23 | In the future, we may consider using a public mailing list, which can be better archived. 24 | 25 | ## Roadmap Planning 26 | 27 | Maintainers will share roadmap and release versions as milestones in GitHub. 28 | 29 | ## Release Management 30 | 31 | The Advisory Board will propose a release management proposal via a GitHub issue and resolve it there. 32 | 33 | ## Other relevant governance resources 34 | 35 | * The ACK [Contributing Guidelines](CONTRIBUTING.md) 36 | * Our [Code of Conduct](CODE_OF_CONDUCT.md) 37 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash # Use bash syntax 2 | 3 | # Set up variables 4 | GO111MODULE=on 5 | 6 | # Build ldflags 7 | VERSION ?= "v0.0.0" 8 | GITCOMMIT=$(shell git rev-parse HEAD) 9 | BUILDDATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') 10 | GO_LDFLAGS=-ldflags "-X main.version=$(VERSION) \ 11 | -X main.buildHash=$(GITCOMMIT) \ 12 | -X main.buildDate=$(BUILDDATE)" 13 | 14 | .PHONY: all test 15 | 16 | all: test 17 | 18 | test: ## Run code tests 19 | go test -v ./... 20 | 21 | help: ## Show this help. 22 | @grep -F -h "##" $(MAKEFILE_LIST) | grep -F -v grep | sed -e 's/\\$$//' \ 23 | | awk -F'[:#]' '{print $$1 = sprintf("%-30s", $$1), $$4}' 24 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | # See the OWNERS docs at https://go.k8s.io/owners 2 | 3 | approvers: 4 | - core-ack-team -------------------------------------------------------------------------------- /OWNERS_ALIASES: -------------------------------------------------------------------------------- 1 | # See the OWNERS docs at https://go.k8s.io/owners#owners_aliases 2 | 3 | aliases: 4 | core-ack-team: 5 | - a-hilaly 6 | - jlbutler 7 | - michaelhtm 8 | - rushmash91 9 | - knottnt 10 | # emeritus-core-ack-team: 11 | # - TiberiuGC 12 | # - jaypipes 13 | # - jljaco 14 | # - mhausenblas 15 | # - RedbackThomson 16 | # - vijtrip2 17 | # - ivelichkovich -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ACK service controller for Amazon Elastic Container Registry (ECR) 2 | 3 | This repository contains source code for the AWS Controllers for Kubernetes 4 | (ACK) service controller for Amazon Elastic Container Registry (ECR). 5 | 6 | Please [log issues][ack-issues] and feedback on the main AWS Controllers for 7 | Kubernetes Github project. 8 | 9 | [ack-issues]: https://github.com/aws/aws-controllers-k8s/issues 10 | 11 | ## Contributing 12 | 13 | We welcome community contributions and pull requests. 14 | 15 | See our [contribution guide](/CONTRIBUTING.md) for more information on how to 16 | report issues, set up a development environment, and submit code. 17 | 18 | We adhere to the [Amazon Open Source Code of Conduct][coc]. 19 | 20 | You can also learn more about our [Governance](/GOVERNANCE.md) structure. 21 | 22 | [coc]: https://aws.github.io/code-of-conduct 23 | 24 | ## License 25 | 26 | This project is [licensed](/LICENSE) under the Apache-2.0 License. 27 | -------------------------------------------------------------------------------- /apis/v1alpha1/ack-generate-metadata.yaml: -------------------------------------------------------------------------------- 1 | ack_generate_info: 2 | build_date: "2025-06-02T18:44:13Z" 3 | build_hash: abd45b45e7726b7893641afaeae805281358e684 4 | go_version: go1.24.2 5 | version: v0.47.2 6 | api_directory_checksum: b1483b12f27211a1ddd173b9d2b87b0d1e6065a4 7 | api_version: v1alpha1 8 | aws_sdk_go_version: v1.32.6 9 | generator_config_info: 10 | file_checksum: e3a1a758e7d029514ae612e827701b5d3bdc2c27 11 | original_file_name: generator.yaml 12 | last_modification: 13 | reason: API generation 14 | -------------------------------------------------------------------------------- /apis/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // +k8s:deepcopy-gen=package 2 | // Package v1alpha1 is the v1alpha1 version of the ecr.services.k8s.aws API. 3 | // +groupName=ecr.services.k8s.aws 4 | package v1alpha1 5 | -------------------------------------------------------------------------------- /apis/v1alpha1/enums.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | type EncryptionType string 19 | 20 | const ( 21 | EncryptionType_AES256 EncryptionType = "AES256" 22 | EncryptionType_KMS EncryptionType = "KMS" 23 | EncryptionType_KMS_DSSE EncryptionType = "KMS_DSSE" 24 | ) 25 | 26 | type FindingSeverity string 27 | 28 | const ( 29 | FindingSeverity_CRITICAL FindingSeverity = "CRITICAL" 30 | FindingSeverity_HIGH FindingSeverity = "HIGH" 31 | FindingSeverity_INFORMATIONAL FindingSeverity = "INFORMATIONAL" 32 | FindingSeverity_LOW FindingSeverity = "LOW" 33 | FindingSeverity_MEDIUM FindingSeverity = "MEDIUM" 34 | FindingSeverity_UNDEFINED FindingSeverity = "UNDEFINED" 35 | ) 36 | 37 | type ImageActionType string 38 | 39 | const ( 40 | ImageActionType_EXPIRE ImageActionType = "EXPIRE" 41 | ) 42 | 43 | type ImageFailureCode string 44 | 45 | const ( 46 | ImageFailureCode_ImageNotFound ImageFailureCode = "ImageNotFound" 47 | ImageFailureCode_ImageReferencedByManifestList ImageFailureCode = "ImageReferencedByManifestList" 48 | ImageFailureCode_ImageTagDoesNotMatchDigest ImageFailureCode = "ImageTagDoesNotMatchDigest" 49 | ImageFailureCode_InvalidImageDigest ImageFailureCode = "InvalidImageDigest" 50 | ImageFailureCode_InvalidImageTag ImageFailureCode = "InvalidImageTag" 51 | ImageFailureCode_KmsError ImageFailureCode = "KmsError" 52 | ImageFailureCode_MissingDigestAndTag ImageFailureCode = "MissingDigestAndTag" 53 | ImageFailureCode_UpstreamAccessDenied ImageFailureCode = "UpstreamAccessDenied" 54 | ImageFailureCode_UpstreamTooManyRequests ImageFailureCode = "UpstreamTooManyRequests" 55 | ImageFailureCode_UpstreamUnavailable ImageFailureCode = "UpstreamUnavailable" 56 | ) 57 | 58 | type ImageTagMutability string 59 | 60 | const ( 61 | ImageTagMutability_IMMUTABLE ImageTagMutability = "IMMUTABLE" 62 | ImageTagMutability_MUTABLE ImageTagMutability = "MUTABLE" 63 | ) 64 | 65 | type LayerAvailability string 66 | 67 | const ( 68 | LayerAvailability_AVAILABLE LayerAvailability = "AVAILABLE" 69 | LayerAvailability_UNAVAILABLE LayerAvailability = "UNAVAILABLE" 70 | ) 71 | 72 | type LayerFailureCode string 73 | 74 | const ( 75 | LayerFailureCode_InvalidLayerDigest LayerFailureCode = "InvalidLayerDigest" 76 | LayerFailureCode_MissingLayerDigest LayerFailureCode = "MissingLayerDigest" 77 | ) 78 | 79 | type LifecyclePolicyPreviewStatus string 80 | 81 | const ( 82 | LifecyclePolicyPreviewStatus_COMPLETE LifecyclePolicyPreviewStatus = "COMPLETE" 83 | LifecyclePolicyPreviewStatus_EXPIRED LifecyclePolicyPreviewStatus = "EXPIRED" 84 | LifecyclePolicyPreviewStatus_FAILED LifecyclePolicyPreviewStatus = "FAILED" 85 | LifecyclePolicyPreviewStatus_IN_PROGRESS LifecyclePolicyPreviewStatus = "IN_PROGRESS" 86 | ) 87 | 88 | type RCTAppliedFor string 89 | 90 | const ( 91 | RCTAppliedFor_PULL_THROUGH_CACHE RCTAppliedFor = "PULL_THROUGH_CACHE" 92 | RCTAppliedFor_REPLICATION RCTAppliedFor = "REPLICATION" 93 | ) 94 | 95 | type ReplicationStatus string 96 | 97 | const ( 98 | ReplicationStatus_COMPLETE ReplicationStatus = "COMPLETE" 99 | ReplicationStatus_FAILED ReplicationStatus = "FAILED" 100 | ReplicationStatus_IN_PROGRESS ReplicationStatus = "IN_PROGRESS" 101 | ) 102 | 103 | type RepositoryFilterType string 104 | 105 | const ( 106 | RepositoryFilterType_PREFIX_MATCH RepositoryFilterType = "PREFIX_MATCH" 107 | ) 108 | 109 | type ScanFrequency string 110 | 111 | const ( 112 | ScanFrequency_CONTINUOUS_SCAN ScanFrequency = "CONTINUOUS_SCAN" 113 | ScanFrequency_MANUAL ScanFrequency = "MANUAL" 114 | ScanFrequency_SCAN_ON_PUSH ScanFrequency = "SCAN_ON_PUSH" 115 | ) 116 | 117 | type ScanStatus string 118 | 119 | const ( 120 | ScanStatus_ACTIVE ScanStatus = "ACTIVE" 121 | ScanStatus_COMPLETE ScanStatus = "COMPLETE" 122 | ScanStatus_FAILED ScanStatus = "FAILED" 123 | ScanStatus_FINDINGS_UNAVAILABLE ScanStatus = "FINDINGS_UNAVAILABLE" 124 | ScanStatus_IN_PROGRESS ScanStatus = "IN_PROGRESS" 125 | ScanStatus_PENDING ScanStatus = "PENDING" 126 | ScanStatus_SCAN_ELIGIBILITY_EXPIRED ScanStatus = "SCAN_ELIGIBILITY_EXPIRED" 127 | ScanStatus_UNSUPPORTED_IMAGE ScanStatus = "UNSUPPORTED_IMAGE" 128 | ) 129 | 130 | type ScanType string 131 | 132 | const ( 133 | ScanType_BASIC ScanType = "BASIC" 134 | ScanType_ENHANCED ScanType = "ENHANCED" 135 | ) 136 | 137 | type ScanningConfigurationFailureCode string 138 | 139 | const ( 140 | ScanningConfigurationFailureCode_REPOSITORY_NOT_FOUND ScanningConfigurationFailureCode = "REPOSITORY_NOT_FOUND" 141 | ) 142 | 143 | type ScanningRepositoryFilterType string 144 | 145 | const ( 146 | ScanningRepositoryFilterType_WILDCARD ScanningRepositoryFilterType = "WILDCARD" 147 | ) 148 | 149 | type TagStatus string 150 | 151 | const ( 152 | TagStatus_ANY TagStatus = "ANY" 153 | TagStatus_TAGGED TagStatus = "TAGGED" 154 | TagStatus_UNTAGGED TagStatus = "UNTAGGED" 155 | ) 156 | 157 | type UpstreamRegistry string 158 | 159 | const ( 160 | UpstreamRegistry_azure_container_registry UpstreamRegistry = "azure-container-registry" 161 | UpstreamRegistry_docker_hub UpstreamRegistry = "docker-hub" 162 | UpstreamRegistry_ecr_public UpstreamRegistry = "ecr-public" 163 | UpstreamRegistry_github_container_registry UpstreamRegistry = "github-container-registry" 164 | UpstreamRegistry_gitlab_container_registry UpstreamRegistry = "gitlab-container-registry" 165 | UpstreamRegistry_k8s UpstreamRegistry = "k8s" 166 | UpstreamRegistry_quay UpstreamRegistry = "quay" 167 | ) 168 | -------------------------------------------------------------------------------- /apis/v1alpha1/generator.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | Repository: 3 | print: 4 | order_by: index 5 | fields: 6 | Name: 7 | is_primary_key: true 8 | is_required: true 9 | from: 10 | operation: CreateRepository 11 | path: RepositoryName 12 | LifecyclePolicy: 13 | from: 14 | operation: PutLifecyclePolicy 15 | path: LifecyclePolicyText 16 | Policy: 17 | from: 18 | operation: SetRepositoryPolicy 19 | path: PolicyText 20 | Tags: 21 | compare: 22 | is_ignored: true 23 | RegistryID: 24 | late_initialize: {} 25 | print: 26 | name: REGISTRY-ID 27 | index: 0 28 | ImageTagMutability: 29 | print: 30 | name: IMAGE-TAG-MUTABILITY 31 | index: 1 32 | renames: 33 | operations: 34 | CreateRepository: 35 | input_fields: 36 | RepositoryName: Name 37 | DeleteRepository: 38 | input_fields: 39 | RepositoryName: Name 40 | DescribeRepositories: 41 | input_fields: 42 | RepositoryName: Name 43 | exceptions: 44 | errors: 45 | 404: 46 | code: RepositoryNotFoundException 47 | list_operation: 48 | match_fields: 49 | - Name 50 | hooks: 51 | delta_pre_compare: 52 | code: customPreCompare(delta, a, b) 53 | sdk_read_many_post_set_output: 54 | template_path: hooks/repository/sdk_read_many_post_set_output.go.tpl 55 | sdk_create_post_set_output: 56 | template_path: hooks/repository/sdk_create_post_set_output.go.tpl 57 | sdk_delete_post_build_request: 58 | template_path: hooks/repository/sdk_delete_post_build_request.go.tpl 59 | update_operation: 60 | custom_method_name: customUpdateRepository 61 | PullThroughCacheRule: 62 | exceptions: 63 | errors: 64 | 404: 65 | code: PullThroughCacheRuleNotFoundException 66 | fields: 67 | ECRRepositoryPrefix: 68 | is_primary_key: true 69 | is_immutable: true 70 | RegistryID: 71 | is_immutable: true 72 | UpstreamRegistryURL: 73 | is_immutable: true 74 | hooks: 75 | sdk_read_many_post_build_request: 76 | template_path: hooks/pull_through_cache_rule/sdk_read_many_post_build_request.go.tpl 77 | list_operation: 78 | match_fields: 79 | - RegistryId 80 | - EcrRepositoryPrefix 81 | tags: 82 | ignore: true 83 | ignore: 84 | resource_names: 85 | - RepositoryCreationTemplate 86 | field_paths: 87 | - CreatePullThroughCacheRuleOutput.CredentialArn 88 | - CreatePullThroughCacheRuleOutput.UpstreamRegistry 89 | - CreatePullThroughCacheRuleInput.UpstreamRegistry 90 | - CreatePullThroughCacheRuleInput.CredentialArn -------------------------------------------------------------------------------- /apis/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | import ( 19 | "k8s.io/apimachinery/pkg/runtime/schema" 20 | "sigs.k8s.io/controller-runtime/pkg/scheme" 21 | ) 22 | 23 | var ( 24 | // GroupVersion is the API Group Version used to register the objects 25 | GroupVersion = schema.GroupVersion{Group: "ecr.services.k8s.aws", Version: "v1alpha1"} 26 | 27 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 28 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 29 | 30 | // AddToScheme adds the types in this group-version to the given scheme. 31 | AddToScheme = SchemeBuilder.AddToScheme 32 | ) 33 | -------------------------------------------------------------------------------- /apis/v1alpha1/pull_through_cache_rule.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | import ( 19 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // PullThroughCacheRuleSpec defines the desired state of PullThroughCacheRule. 24 | // 25 | // The details of a pull through cache rule. 26 | type PullThroughCacheRuleSpec struct { 27 | 28 | // The repository name prefix to use when caching images from the source registry. 29 | // 30 | // Regex Pattern: `^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$` 31 | // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable once set" 32 | // +kubebuilder:validation:Required 33 | ECRRepositoryPrefix *string `json:"ecrRepositoryPrefix"` 34 | // The Amazon Web Services account ID associated with the registry to create 35 | // the pull through cache rule for. If you do not specify a registry, the default 36 | // registry is assumed. 37 | // 38 | // Regex Pattern: `^[0-9]{12}$` 39 | // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable once set" 40 | RegistryID *string `json:"registryID,omitempty"` 41 | // The registry URL of the upstream public registry to use as the source for 42 | // the pull through cache rule. The following is the syntax to use for each 43 | // supported upstream registry. 44 | // 45 | // - Amazon ECR Public (ecr-public) - public.ecr.aws 46 | // 47 | // - Docker Hub (docker-hub) - registry-1.docker.io 48 | // 49 | // - Quay (quay) - quay.io 50 | // 51 | // - Kubernetes (k8s) - registry.k8s.io 52 | // 53 | // - GitHub Container Registry (github-container-registry) - ghcr.io 54 | // 55 | // - Microsoft Azure Container Registry (azure-container-registry) - .azurecr.io 56 | // 57 | // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable once set" 58 | // +kubebuilder:validation:Required 59 | UpstreamRegistryURL *string `json:"upstreamRegistryURL"` 60 | } 61 | 62 | // PullThroughCacheRuleStatus defines the observed state of PullThroughCacheRule 63 | type PullThroughCacheRuleStatus struct { 64 | // All CRs managed by ACK have a common `Status.ACKResourceMetadata` member 65 | // that is used to contain resource sync state, account ownership, 66 | // constructed ARN for the resource 67 | // +kubebuilder:validation:Optional 68 | ACKResourceMetadata *ackv1alpha1.ResourceMetadata `json:"ackResourceMetadata"` 69 | // All CRs managed by ACK have a common `Status.Conditions` member that 70 | // contains a collection of `ackv1alpha1.Condition` objects that describe 71 | // the various terminal states of the CR and its backend AWS service API 72 | // resource 73 | // +kubebuilder:validation:Optional 74 | Conditions []*ackv1alpha1.Condition `json:"conditions"` 75 | // The date and time, in JavaScript date format, when the pull through cache 76 | // rule was created. 77 | // +kubebuilder:validation:Optional 78 | CreatedAt *metav1.Time `json:"createdAt,omitempty"` 79 | } 80 | 81 | // PullThroughCacheRule is the Schema for the PullThroughCacheRules API 82 | // +kubebuilder:object:root=true 83 | // +kubebuilder:subresource:status 84 | type PullThroughCacheRule struct { 85 | metav1.TypeMeta `json:",inline"` 86 | metav1.ObjectMeta `json:"metadata,omitempty"` 87 | Spec PullThroughCacheRuleSpec `json:"spec,omitempty"` 88 | Status PullThroughCacheRuleStatus `json:"status,omitempty"` 89 | } 90 | 91 | // PullThroughCacheRuleList contains a list of PullThroughCacheRule 92 | // +kubebuilder:object:root=true 93 | type PullThroughCacheRuleList struct { 94 | metav1.TypeMeta `json:",inline"` 95 | metav1.ListMeta `json:"metadata,omitempty"` 96 | Items []PullThroughCacheRule `json:"items"` 97 | } 98 | 99 | func init() { 100 | SchemeBuilder.Register(&PullThroughCacheRule{}, &PullThroughCacheRuleList{}) 101 | } 102 | -------------------------------------------------------------------------------- /apis/v1alpha1/repository.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | import ( 19 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // RepositorySpec defines the desired state of Repository. 24 | // 25 | // An object representing a repository. 26 | type RepositorySpec struct { 27 | 28 | // The encryption configuration for the repository. This determines how the 29 | // contents of your repository are encrypted at rest. 30 | EncryptionConfiguration *EncryptionConfiguration `json:"encryptionConfiguration,omitempty"` 31 | // The image scanning configuration for the repository. This determines whether 32 | // images are scanned for known vulnerabilities after being pushed to the repository. 33 | ImageScanningConfiguration *ImageScanningConfiguration `json:"imageScanningConfiguration,omitempty"` 34 | // The tag mutability setting for the repository. If this parameter is omitted, 35 | // the default setting of MUTABLE will be used which will allow image tags to 36 | // be overwritten. If IMMUTABLE is specified, all image tags within the repository 37 | // will be immutable which will prevent them from being overwritten. 38 | ImageTagMutability *string `json:"imageTagMutability,omitempty"` 39 | // The JSON repository policy text to apply to the repository. 40 | LifecyclePolicy *string `json:"lifecyclePolicy,omitempty"` 41 | // The name to use for the repository. The repository name may be specified 42 | // on its own (such as nginx-web-app) or it can be prepended with a namespace 43 | // to group the repository into a category (such as project-a/nginx-web-app). 44 | // 45 | // The repository name must start with a letter and can only contain lowercase 46 | // letters, numbers, hyphens, underscores, and forward slashes. 47 | // 48 | // Regex Pattern: `^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$` 49 | // +kubebuilder:validation:Required 50 | Name *string `json:"name"` 51 | // The JSON repository policy text to apply to the repository. For more information, 52 | // see Amazon ECR repository policies (https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-policy-examples.html) 53 | // in the Amazon Elastic Container Registry User Guide. 54 | Policy *string `json:"policy,omitempty"` 55 | // The Amazon Web Services account ID associated with the registry to create 56 | // the repository. If you do not specify a registry, the default registry is 57 | // assumed. 58 | // 59 | // Regex Pattern: `^[0-9]{12}$` 60 | RegistryID *string `json:"registryID,omitempty"` 61 | // The metadata that you apply to the repository to help you categorize and 62 | // organize them. Each tag consists of a key and an optional value, both of 63 | // which you define. Tag keys can have a maximum character length of 128 characters, 64 | // and tag values can have a maximum length of 256 characters. 65 | Tags []*Tag `json:"tags,omitempty"` 66 | } 67 | 68 | // RepositoryStatus defines the observed state of Repository 69 | type RepositoryStatus struct { 70 | // All CRs managed by ACK have a common `Status.ACKResourceMetadata` member 71 | // that is used to contain resource sync state, account ownership, 72 | // constructed ARN for the resource 73 | // +kubebuilder:validation:Optional 74 | ACKResourceMetadata *ackv1alpha1.ResourceMetadata `json:"ackResourceMetadata"` 75 | // All CRs managed by ACK have a common `Status.Conditions` member that 76 | // contains a collection of `ackv1alpha1.Condition` objects that describe 77 | // the various terminal states of the CR and its backend AWS service API 78 | // resource 79 | // +kubebuilder:validation:Optional 80 | Conditions []*ackv1alpha1.Condition `json:"conditions"` 81 | // The date and time, in JavaScript date format, when the repository was created. 82 | // +kubebuilder:validation:Optional 83 | CreatedAt *metav1.Time `json:"createdAt,omitempty"` 84 | // The URI for the repository. You can use this URI for container image push 85 | // and pull operations. 86 | // +kubebuilder:validation:Optional 87 | RepositoryURI *string `json:"repositoryURI,omitempty"` 88 | } 89 | 90 | // Repository is the Schema for the Repositories API 91 | // +kubebuilder:object:root=true 92 | // +kubebuilder:subresource:status 93 | // +kubebuilder:printcolumn:name="REGISTRY-ID",type=string,priority=0,JSONPath=`.spec.registryID` 94 | // +kubebuilder:printcolumn:name="IMAGE-TAG-MUTABILITY",type=string,priority=0,JSONPath=`.spec.imageTagMutability` 95 | // +kubebuilder:printcolumn:name="Synced",type="string",priority=0,JSONPath=".status.conditions[?(@.type==\"ACK.ResourceSynced\")].status" 96 | type Repository struct { 97 | metav1.TypeMeta `json:",inline"` 98 | metav1.ObjectMeta `json:"metadata,omitempty"` 99 | Spec RepositorySpec `json:"spec,omitempty"` 100 | Status RepositoryStatus `json:"status,omitempty"` 101 | } 102 | 103 | // RepositoryList contains a list of Repository 104 | // +kubebuilder:object:root=true 105 | type RepositoryList struct { 106 | metav1.TypeMeta `json:",inline"` 107 | metav1.ListMeta `json:"metadata,omitempty"` 108 | Items []Repository `json:"items"` 109 | } 110 | 111 | func init() { 112 | SchemeBuilder.Register(&Repository{}, &RepositoryList{}) 113 | } 114 | -------------------------------------------------------------------------------- /apis/v1alpha1/types.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package v1alpha1 17 | 18 | import ( 19 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 20 | "github.com/aws/aws-sdk-go/aws" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | ) 23 | 24 | // Hack to avoid import errors during build... 25 | var ( 26 | _ = &metav1.Time{} 27 | _ = &aws.JSONValue{} 28 | _ = ackv1alpha1.AWSAccountID("") 29 | ) 30 | 31 | // The image details of the Amazon ECR container image. 32 | type AWSECRContainerImageDetails struct { 33 | Registry *string `json:"registry,omitempty"` 34 | RepositoryName *string `json:"repositoryName,omitempty"` 35 | } 36 | 37 | // The encryption configuration for the repository. This determines how the 38 | // contents of your repository are encrypted at rest. 39 | // 40 | // By default, when no encryption configuration is set or the AES256 encryption 41 | // type is used, Amazon ECR uses server-side encryption with Amazon S3-managed 42 | // encryption keys which encrypts your data at rest using an AES256 encryption 43 | // algorithm. This does not require any action on your part. 44 | // 45 | // For more control over the encryption of the contents of your repository, 46 | // you can use server-side encryption with Key Management Service key stored 47 | // in Key Management Service (KMS) to encrypt your images. For more information, 48 | // see Amazon ECR encryption at rest (https://docs.aws.amazon.com/AmazonECR/latest/userguide/encryption-at-rest.html) 49 | // in the Amazon Elastic Container Registry User Guide. 50 | type EncryptionConfiguration struct { 51 | EncryptionType *string `json:"encryptionType,omitempty"` 52 | KMSKey *string `json:"kmsKey,omitempty"` 53 | } 54 | 55 | // The encryption configuration to associate with the repository creation template. 56 | type EncryptionConfigurationForRepositoryCreationTemplate struct { 57 | EncryptionType *string `json:"encryptionType,omitempty"` 58 | } 59 | 60 | // The details of an enhanced image scan. This is returned when enhanced scanning 61 | // is enabled for your private registry. 62 | type EnhancedImageScanFinding struct { 63 | AWSAccountID *string `json:"awsAccountID,omitempty"` 64 | } 65 | 66 | // An object representing an Amazon ECR image. 67 | type Image struct { 68 | RegistryID *string `json:"registryID,omitempty"` 69 | RepositoryName *string `json:"repositoryName,omitempty"` 70 | } 71 | 72 | // An object that describes an image returned by a DescribeImages operation. 73 | type ImageDetail struct { 74 | RegistryID *string `json:"registryID,omitempty"` 75 | RepositoryName *string `json:"repositoryName,omitempty"` 76 | } 77 | 78 | // The status of the replication process for an image. 79 | type ImageReplicationStatus struct { 80 | RegistryID *string `json:"registryID,omitempty"` 81 | } 82 | 83 | // Contains information about an image scan finding. 84 | type ImageScanFinding struct { 85 | URI *string `json:"uri,omitempty"` 86 | } 87 | 88 | // The image scanning configuration for a repository. 89 | type ImageScanningConfiguration struct { 90 | ScanOnPush *bool `json:"scanOnPush,omitempty"` 91 | } 92 | 93 | // Information about a package vulnerability finding. 94 | type PackageVulnerabilityDetails struct { 95 | SourceURL *string `json:"sourceURL,omitempty"` 96 | } 97 | 98 | // The details of a pull through cache rule. 99 | type PullThroughCacheRule_SDK struct { 100 | CreatedAt *metav1.Time `json:"createdAt,omitempty"` 101 | CredentialARN *string `json:"credentialARN,omitempty"` 102 | ECRRepositoryPrefix *string `json:"ecrRepositoryPrefix,omitempty"` 103 | RegistryID *string `json:"registryID,omitempty"` 104 | UpdatedAt *metav1.Time `json:"updatedAt,omitempty"` 105 | UpstreamRegistry *string `json:"upstreamRegistry,omitempty"` 106 | UpstreamRegistryURL *string `json:"upstreamRegistryURL,omitempty"` 107 | } 108 | 109 | // Details about the recommended course of action to remediate the finding. 110 | type Recommendation struct { 111 | URL *string `json:"url,omitempty"` 112 | } 113 | 114 | // An array of objects representing the destination for a replication rule. 115 | type ReplicationDestination struct { 116 | RegistryID *string `json:"registryID,omitempty"` 117 | } 118 | 119 | // The details of the repository creation template associated with the request. 120 | type RepositoryCreationTemplate struct { 121 | ImageTagMutability *string `json:"imageTagMutability,omitempty"` 122 | RepositoryPolicy *string `json:"repositoryPolicy,omitempty"` 123 | ResourceTags []*Tag `json:"resourceTags,omitempty"` 124 | } 125 | 126 | // The details of the scanning configuration for a repository. 127 | type RepositoryScanningConfiguration struct { 128 | RepositoryARN *string `json:"repositoryARN,omitempty"` 129 | RepositoryName *string `json:"repositoryName,omitempty"` 130 | ScanOnPush *bool `json:"scanOnPush,omitempty"` 131 | } 132 | 133 | // The details about any failures associated with the scanning configuration 134 | // of a repository. 135 | type RepositoryScanningConfigurationFailure struct { 136 | RepositoryName *string `json:"repositoryName,omitempty"` 137 | } 138 | 139 | // An object representing a repository. 140 | type Repository_SDK struct { 141 | CreatedAt *metav1.Time `json:"createdAt,omitempty"` 142 | // The encryption configuration for the repository. This determines how the 143 | // contents of your repository are encrypted at rest. 144 | // 145 | // By default, when no encryption configuration is set or the AES256 encryption 146 | // type is used, Amazon ECR uses server-side encryption with Amazon S3-managed 147 | // encryption keys which encrypts your data at rest using an AES256 encryption 148 | // algorithm. This does not require any action on your part. 149 | // 150 | // For more control over the encryption of the contents of your repository, 151 | // you can use server-side encryption with Key Management Service key stored 152 | // in Key Management Service (KMS) to encrypt your images. For more information, 153 | // see Amazon ECR encryption at rest (https://docs.aws.amazon.com/AmazonECR/latest/userguide/encryption-at-rest.html) 154 | // in the Amazon Elastic Container Registry User Guide. 155 | EncryptionConfiguration *EncryptionConfiguration `json:"encryptionConfiguration,omitempty"` 156 | // The image scanning configuration for a repository. 157 | ImageScanningConfiguration *ImageScanningConfiguration `json:"imageScanningConfiguration,omitempty"` 158 | ImageTagMutability *string `json:"imageTagMutability,omitempty"` 159 | RegistryID *string `json:"registryID,omitempty"` 160 | RepositoryARN *string `json:"repositoryARN,omitempty"` 161 | RepositoryName *string `json:"repositoryName,omitempty"` 162 | RepositoryURI *string `json:"repositoryURI,omitempty"` 163 | } 164 | 165 | // The metadata to apply to a resource to help you categorize and organize them. 166 | // Each tag consists of a key and a value, both of which you define. Tag keys 167 | // can have a maximum character length of 128 characters, and tag values can 168 | // have a maximum length of 256 characters. 169 | type Tag struct { 170 | Key *string `json:"key,omitempty"` 171 | Value *string `json:"value,omitempty"` 172 | } 173 | -------------------------------------------------------------------------------- /cmd/controller/main.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package main 17 | 18 | import ( 19 | "context" 20 | "os" 21 | 22 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 23 | ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" 24 | ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" 25 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 26 | ackrtutil "github.com/aws-controllers-k8s/runtime/pkg/util" 27 | ackrtwebhook "github.com/aws-controllers-k8s/runtime/pkg/webhook" 28 | flag "github.com/spf13/pflag" 29 | "k8s.io/apimachinery/pkg/runtime" 30 | "k8s.io/apimachinery/pkg/runtime/schema" 31 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 32 | ctrlrt "sigs.k8s.io/controller-runtime" 33 | ctrlrtcache "sigs.k8s.io/controller-runtime/pkg/cache" 34 | ctrlrthealthz "sigs.k8s.io/controller-runtime/pkg/healthz" 35 | ctrlrtmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" 36 | metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" 37 | ctrlrtwebhook "sigs.k8s.io/controller-runtime/pkg/webhook" 38 | 39 | svctypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1" 40 | svcresource "github.com/aws-controllers-k8s/ecr-controller/pkg/resource" 41 | 42 | _ "github.com/aws-controllers-k8s/ecr-controller/pkg/resource/pull_through_cache_rule" 43 | _ "github.com/aws-controllers-k8s/ecr-controller/pkg/resource/repository" 44 | 45 | "github.com/aws-controllers-k8s/ecr-controller/pkg/version" 46 | ) 47 | 48 | var ( 49 | awsServiceAPIGroup = "ecr.services.k8s.aws" 50 | awsServiceAlias = "ecr" 51 | scheme = runtime.NewScheme() 52 | setupLog = ctrlrt.Log.WithName("setup") 53 | ) 54 | 55 | func init() { 56 | _ = clientgoscheme.AddToScheme(scheme) 57 | 58 | _ = svctypes.AddToScheme(scheme) 59 | _ = ackv1alpha1.AddToScheme(scheme) 60 | } 61 | 62 | func main() { 63 | var ackCfg ackcfg.Config 64 | ackCfg.BindFlags() 65 | flag.Parse() 66 | ackCfg.SetupLogger() 67 | 68 | managerFactories := svcresource.GetManagerFactories() 69 | resourceGVKs := make([]schema.GroupVersionKind, 0, len(managerFactories)) 70 | for _, mf := range managerFactories { 71 | resourceGVKs = append(resourceGVKs, mf.ResourceDescriptor().GroupVersionKind()) 72 | } 73 | 74 | ctx := context.Background() 75 | if err := ackCfg.Validate(ctx, ackcfg.WithGVKs(resourceGVKs)); err != nil { 76 | setupLog.Error( 77 | err, "Unable to create controller manager", 78 | "aws.service", awsServiceAlias, 79 | ) 80 | os.Exit(1) 81 | } 82 | 83 | host, port, err := ackrtutil.GetHostPort(ackCfg.WebhookServerAddr) 84 | if err != nil { 85 | setupLog.Error( 86 | err, "Unable to parse webhook server address.", 87 | "aws.service", awsServiceAlias, 88 | ) 89 | os.Exit(1) 90 | } 91 | 92 | watchNamespaces := make(map[string]ctrlrtcache.Config, 0) 93 | namespaces, err := ackCfg.GetWatchNamespaces() 94 | if err != nil { 95 | setupLog.Error( 96 | err, "Unable to parse watch namespaces.", 97 | "aws.service", ackCfg.WatchNamespace, 98 | ) 99 | os.Exit(1) 100 | } 101 | 102 | for _, namespace := range namespaces { 103 | watchNamespaces[namespace] = ctrlrtcache.Config{} 104 | } 105 | watchSelectors, err := ackCfg.ParseWatchSelectors() 106 | if err != nil { 107 | setupLog.Error( 108 | err, "Unable to parse watch selectors.", 109 | "aws.service", awsServiceAlias, 110 | ) 111 | os.Exit(1) 112 | } 113 | mgr, err := ctrlrt.NewManager(ctrlrt.GetConfigOrDie(), ctrlrt.Options{ 114 | Scheme: scheme, 115 | Cache: ctrlrtcache.Options{ 116 | Scheme: scheme, 117 | DefaultNamespaces: watchNamespaces, 118 | DefaultLabelSelector: watchSelectors, 119 | }, 120 | WebhookServer: &ctrlrtwebhook.DefaultServer{ 121 | Options: ctrlrtwebhook.Options{ 122 | Port: port, 123 | Host: host, 124 | }, 125 | }, 126 | Metrics: metricsserver.Options{BindAddress: ackCfg.MetricsAddr}, 127 | LeaderElection: ackCfg.EnableLeaderElection, 128 | LeaderElectionID: "ack-" + awsServiceAPIGroup, 129 | LeaderElectionNamespace: ackCfg.LeaderElectionNamespace, 130 | HealthProbeBindAddress: ackCfg.HealthzAddr, 131 | LivenessEndpointName: "/healthz", 132 | ReadinessEndpointName: "/readyz", 133 | }) 134 | if err != nil { 135 | setupLog.Error( 136 | err, "unable to create controller manager", 137 | "aws.service", awsServiceAlias, 138 | ) 139 | os.Exit(1) 140 | } 141 | 142 | stopChan := ctrlrt.SetupSignalHandler() 143 | 144 | setupLog.Info( 145 | "initializing service controller", 146 | "aws.service", awsServiceAlias, 147 | ) 148 | sc := ackrt.NewServiceController( 149 | awsServiceAlias, awsServiceAPIGroup, 150 | acktypes.VersionInfo{ 151 | version.GitCommit, 152 | version.GitVersion, 153 | version.BuildDate, 154 | }, 155 | ).WithLogger( 156 | ctrlrt.Log, 157 | ).WithResourceManagerFactories( 158 | svcresource.GetManagerFactories(), 159 | ).WithPrometheusRegistry( 160 | ctrlrtmetrics.Registry, 161 | ) 162 | 163 | if ackCfg.EnableWebhookServer { 164 | webhooks := ackrtwebhook.GetWebhooks() 165 | for _, webhook := range webhooks { 166 | if err := webhook.Setup(mgr); err != nil { 167 | setupLog.Error( 168 | err, "unable to register webhook "+webhook.UID(), 169 | "aws.service", awsServiceAlias, 170 | ) 171 | } 172 | } 173 | } 174 | 175 | if err = sc.BindControllerManager(mgr, ackCfg); err != nil { 176 | setupLog.Error( 177 | err, "unable bind to controller manager to service controller", 178 | "aws.service", awsServiceAlias, 179 | ) 180 | os.Exit(1) 181 | } 182 | 183 | if err = mgr.AddHealthzCheck("health", ctrlrthealthz.Ping); err != nil { 184 | setupLog.Error( 185 | err, "unable to set up health check", 186 | "aws.service", awsServiceAlias, 187 | ) 188 | os.Exit(1) 189 | } 190 | if err = mgr.AddReadyzCheck("check", ctrlrthealthz.Ping); err != nil { 191 | setupLog.Error( 192 | err, "unable to set up ready check", 193 | "aws.service", awsServiceAlias, 194 | ) 195 | os.Exit(1) 196 | } 197 | 198 | setupLog.Info( 199 | "starting manager", 200 | "aws.service", awsServiceAlias, 201 | ) 202 | if err := mgr.Start(stopChan); err != nil { 203 | setupLog.Error( 204 | err, "unable to start controller manager", 205 | "aws.service", awsServiceAlias, 206 | ) 207 | os.Exit(1) 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /config/controller/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: ack-system 5 | --- 6 | apiVersion: apps/v1 7 | kind: Deployment 8 | metadata: 9 | name: ack-ecr-controller 10 | namespace: ack-system 11 | labels: 12 | app.kubernetes.io/name: ack-ecr-controller 13 | app.kubernetes.io/part-of: ack-system 14 | spec: 15 | selector: 16 | matchLabels: 17 | app.kubernetes.io/name: ack-ecr-controller 18 | replicas: 1 19 | template: 20 | metadata: 21 | labels: 22 | app.kubernetes.io/name: ack-ecr-controller 23 | spec: 24 | containers: 25 | - command: 26 | - ./bin/controller 27 | args: 28 | - --aws-region 29 | - "$(AWS_REGION)" 30 | - --aws-endpoint-url 31 | - "$(AWS_ENDPOINT_URL)" 32 | - --enable-development-logging=$(ACK_ENABLE_DEVELOPMENT_LOGGING) 33 | - --log-level 34 | - "$(ACK_LOG_LEVEL)" 35 | - --resource-tags 36 | - "$(ACK_RESOURCE_TAGS)" 37 | - --watch-namespace 38 | - "$(ACK_WATCH_NAMESPACE)" 39 | - --enable-leader-election=$(ENABLE_LEADER_ELECTION) 40 | - --leader-election-namespace 41 | - "$(LEADER_ELECTION_NAMESPACE)" 42 | - --reconcile-default-max-concurrent-syncs 43 | - "$(RECONCILE_DEFAULT_MAX_CONCURRENT_SYNCS)" 44 | - --feature-gates 45 | - "$(FEATURE_GATES)" 46 | image: controller:latest 47 | name: controller 48 | ports: 49 | - name: http 50 | containerPort: 8080 51 | resources: 52 | limits: 53 | cpu: 100m 54 | memory: 300Mi 55 | requests: 56 | cpu: 100m 57 | memory: 200Mi 58 | env: 59 | - name: ACK_SYSTEM_NAMESPACE 60 | valueFrom: 61 | fieldRef: 62 | fieldPath: metadata.namespace 63 | - name: AWS_REGION 64 | value: "" 65 | - name: AWS_ENDPOINT_URL 66 | value: "" 67 | - name: ACK_WATCH_NAMESPACE 68 | value: "" 69 | - name: ACK_ENABLE_DEVELOPMENT_LOGGING 70 | value: "false" 71 | - name: ACK_LOG_LEVEL 72 | value: "info" 73 | - name: ACK_RESOURCE_TAGS 74 | value: "services.k8s.aws/controller-version=%CONTROLLER_SERVICE%-%CONTROLLER_VERSION%,services.k8s.aws/namespace=%K8S_NAMESPACE%" 75 | - name: ENABLE_LEADER_ELECTION 76 | value: "false" 77 | - name: LEADER_ELECTION_NAMESPACE 78 | value: "ack-system" 79 | - name: "RECONCILE_DEFAULT_MAX_CONCURRENT_SYNCS" 80 | value: "1" 81 | - name: "FEATURE_GATES" 82 | value: "" 83 | securityContext: 84 | allowPrivilegeEscalation: false 85 | privileged: false 86 | runAsNonRoot: true 87 | capabilities: 88 | drop: 89 | - ALL 90 | livenessProbe: 91 | httpGet: 92 | path: /healthz 93 | port: 8081 94 | initialDelaySeconds: 15 95 | periodSeconds: 20 96 | readinessProbe: 97 | httpGet: 98 | path: /readyz 99 | port: 8081 100 | initialDelaySeconds: 5 101 | periodSeconds: 10 102 | securityContext: 103 | seccompProfile: 104 | type: RuntimeDefault 105 | terminationGracePeriodSeconds: 10 106 | serviceAccountName: ack-ecr-controller 107 | hostIPC: false 108 | hostPID: false 109 | hostNetwork: false 110 | dnsPolicy: ClusterFirst 111 | -------------------------------------------------------------------------------- /config/controller/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - deployment.yaml 3 | - service.yaml 4 | apiVersion: kustomize.config.k8s.io/v1beta1 5 | kind: Kustomization 6 | images: 7 | - name: controller 8 | newName: public.ecr.aws/aws-controllers-k8s/ecr-controller 9 | newTag: 1.0.30 10 | -------------------------------------------------------------------------------- /config/controller/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: ack-ecr-metrics-service 5 | namespace: ack-system 6 | spec: 7 | selector: 8 | app.kubernetes.io/name: ack-ecr-controller 9 | ports: 10 | - name: metricsport 11 | port: 8080 12 | targetPort: http 13 | protocol: TCP 14 | type: NodePort 15 | -------------------------------------------------------------------------------- /config/crd/bases/ecr.services.k8s.aws_pullthroughcacherules.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.16.2 7 | name: pullthroughcacherules.ecr.services.k8s.aws 8 | spec: 9 | group: ecr.services.k8s.aws 10 | names: 11 | kind: PullThroughCacheRule 12 | listKind: PullThroughCacheRuleList 13 | plural: pullthroughcacherules 14 | singular: pullthroughcacherule 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: PullThroughCacheRule is the Schema for the PullThroughCacheRules 21 | API 22 | properties: 23 | apiVersion: 24 | description: |- 25 | APIVersion defines the versioned schema of this representation of an object. 26 | Servers should convert recognized schemas to the latest internal value, and 27 | may reject unrecognized values. 28 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 29 | type: string 30 | kind: 31 | description: |- 32 | Kind is a string value representing the REST resource this object represents. 33 | Servers may infer this from the endpoint the client submits requests to. 34 | Cannot be updated. 35 | In CamelCase. 36 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 37 | type: string 38 | metadata: 39 | type: object 40 | spec: 41 | description: |- 42 | PullThroughCacheRuleSpec defines the desired state of PullThroughCacheRule. 43 | 44 | The details of a pull through cache rule. 45 | properties: 46 | ecrRepositoryPrefix: 47 | description: |- 48 | The repository name prefix to use when caching images from the source registry. 49 | 50 | Regex Pattern: `^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$` 51 | type: string 52 | x-kubernetes-validations: 53 | - message: Value is immutable once set 54 | rule: self == oldSelf 55 | registryID: 56 | description: |- 57 | The Amazon Web Services account ID associated with the registry to create 58 | the pull through cache rule for. If you do not specify a registry, the default 59 | registry is assumed. 60 | 61 | Regex Pattern: `^[0-9]{12}$` 62 | type: string 63 | x-kubernetes-validations: 64 | - message: Value is immutable once set 65 | rule: self == oldSelf 66 | upstreamRegistryURL: 67 | description: |- 68 | The registry URL of the upstream public registry to use as the source for 69 | the pull through cache rule. The following is the syntax to use for each 70 | supported upstream registry. 71 | 72 | * Amazon ECR Public (ecr-public) - public.ecr.aws 73 | 74 | * Docker Hub (docker-hub) - registry-1.docker.io 75 | 76 | * Quay (quay) - quay.io 77 | 78 | * Kubernetes (k8s) - registry.k8s.io 79 | 80 | * GitHub Container Registry (github-container-registry) - ghcr.io 81 | 82 | * Microsoft Azure Container Registry (azure-container-registry) - .azurecr.io 83 | type: string 84 | x-kubernetes-validations: 85 | - message: Value is immutable once set 86 | rule: self == oldSelf 87 | required: 88 | - ecrRepositoryPrefix 89 | - upstreamRegistryURL 90 | type: object 91 | status: 92 | description: PullThroughCacheRuleStatus defines the observed state of 93 | PullThroughCacheRule 94 | properties: 95 | ackResourceMetadata: 96 | description: |- 97 | All CRs managed by ACK have a common `Status.ACKResourceMetadata` member 98 | that is used to contain resource sync state, account ownership, 99 | constructed ARN for the resource 100 | properties: 101 | arn: 102 | description: |- 103 | ARN is the Amazon Resource Name for the resource. This is a 104 | globally-unique identifier and is set only by the ACK service controller 105 | once the controller has orchestrated the creation of the resource OR 106 | when it has verified that an "adopted" resource (a resource where the 107 | ARN annotation was set by the Kubernetes user on the CR) exists and 108 | matches the supplied CR's Spec field values. 109 | https://github.com/aws/aws-controllers-k8s/issues/270 110 | type: string 111 | ownerAccountID: 112 | description: |- 113 | OwnerAccountID is the AWS Account ID of the account that owns the 114 | backend AWS service API resource. 115 | type: string 116 | region: 117 | description: Region is the AWS region in which the resource exists 118 | or will exist. 119 | type: string 120 | required: 121 | - ownerAccountID 122 | - region 123 | type: object 124 | conditions: 125 | description: |- 126 | All CRs managed by ACK have a common `Status.Conditions` member that 127 | contains a collection of `ackv1alpha1.Condition` objects that describe 128 | the various terminal states of the CR and its backend AWS service API 129 | resource 130 | items: 131 | description: |- 132 | Condition is the common struct used by all CRDs managed by ACK service 133 | controllers to indicate terminal states of the CR and its backend AWS 134 | service API resource 135 | properties: 136 | lastTransitionTime: 137 | description: Last time the condition transitioned from one status 138 | to another. 139 | format: date-time 140 | type: string 141 | message: 142 | description: A human readable message indicating details about 143 | the transition. 144 | type: string 145 | reason: 146 | description: The reason for the condition's last transition. 147 | type: string 148 | status: 149 | description: Status of the condition, one of True, False, Unknown. 150 | type: string 151 | type: 152 | description: Type is the type of the Condition 153 | type: string 154 | required: 155 | - status 156 | - type 157 | type: object 158 | type: array 159 | createdAt: 160 | description: |- 161 | The date and time, in JavaScript date format, when the pull through cache 162 | rule was created. 163 | format: date-time 164 | type: string 165 | type: object 166 | type: object 167 | served: true 168 | storage: true 169 | subresources: 170 | status: {} 171 | -------------------------------------------------------------------------------- /config/crd/common/bases/services.k8s.aws_fieldexports.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.16.2 7 | name: fieldexports.services.k8s.aws 8 | spec: 9 | group: services.k8s.aws 10 | names: 11 | kind: FieldExport 12 | listKind: FieldExportList 13 | plural: fieldexports 14 | singular: fieldexport 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: FieldExport is the schema for the FieldExport API. 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: FieldExportSpec defines the desired state of the FieldExport. 41 | properties: 42 | from: 43 | description: |- 44 | ResourceFieldSelector provides the values necessary to identify an individual 45 | field on an individual K8s resource. 46 | properties: 47 | path: 48 | type: string 49 | resource: 50 | description: |- 51 | NamespacedResource provides all the values necessary to identify an ACK 52 | resource of a given type (within the same namespace as the custom resource 53 | containing this type). 54 | properties: 55 | group: 56 | type: string 57 | kind: 58 | type: string 59 | name: 60 | type: string 61 | required: 62 | - group 63 | - kind 64 | - name 65 | type: object 66 | required: 67 | - path 68 | - resource 69 | type: object 70 | to: 71 | description: |- 72 | FieldExportTarget provides the values necessary to identify the 73 | output path for a field export. 74 | properties: 75 | key: 76 | description: Key overrides the default value (`.`) 77 | for the FieldExport target 78 | type: string 79 | kind: 80 | description: |- 81 | FieldExportOutputType represents all types that can be produced by a field 82 | export operation 83 | enum: 84 | - configmap 85 | - secret 86 | type: string 87 | name: 88 | type: string 89 | namespace: 90 | description: Namespace is marked as optional, so we cannot compose 91 | `NamespacedName` 92 | type: string 93 | required: 94 | - kind 95 | - name 96 | type: object 97 | required: 98 | - from 99 | - to 100 | type: object 101 | status: 102 | description: FieldExportStatus defines the observed status of the FieldExport. 103 | properties: 104 | conditions: 105 | description: |- 106 | A collection of `ackv1alpha1.Condition` objects that describe the various 107 | recoverable states of the field CR 108 | items: 109 | description: |- 110 | Condition is the common struct used by all CRDs managed by ACK service 111 | controllers to indicate terminal states of the CR and its backend AWS 112 | service API resource 113 | properties: 114 | lastTransitionTime: 115 | description: Last time the condition transitioned from one status 116 | to another. 117 | format: date-time 118 | type: string 119 | message: 120 | description: A human readable message indicating details about 121 | the transition. 122 | type: string 123 | reason: 124 | description: The reason for the condition's last transition. 125 | type: string 126 | status: 127 | description: Status of the condition, one of True, False, Unknown. 128 | type: string 129 | type: 130 | description: Type is the type of the Condition 131 | type: string 132 | required: 133 | - status 134 | - type 135 | type: object 136 | type: array 137 | required: 138 | - conditions 139 | type: object 140 | type: object 141 | served: true 142 | storage: true 143 | subresources: 144 | status: {} 145 | -------------------------------------------------------------------------------- /config/crd/common/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Code generated in runtime. DO NOT EDIT. 2 | 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - bases/services.k8s.aws_adoptedresources.yaml 7 | - bases/services.k8s.aws_fieldexports.yaml 8 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - common 5 | - bases/ecr.services.k8s.aws_pullthroughcacherules.yaml 6 | - bases/ecr.services.k8s.aws_repositories.yaml 7 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | # namespace: 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | # namePrefix: 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | resources: 16 | - ../crd 17 | - ../rbac 18 | - ../controller 19 | 20 | patchesStrategicMerge: 21 | -------------------------------------------------------------------------------- /config/iam/recommended-policy-arn: -------------------------------------------------------------------------------- 1 | arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess 2 | -------------------------------------------------------------------------------- /config/overlays/namespaced/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../default 3 | patches: 4 | - path: role.json 5 | target: 6 | group: rbac.authorization.k8s.io 7 | version: v1 8 | kind: ClusterRole 9 | name: ack-ecr-controller 10 | - path: role-binding.json 11 | target: 12 | group: rbac.authorization.k8s.io 13 | version: v1 14 | kind: ClusterRoleBinding 15 | name: ack-ecr-controller-rolebinding -------------------------------------------------------------------------------- /config/overlays/namespaced/role-binding.json: -------------------------------------------------------------------------------- 1 | [{"op": "replace", "path": "/kind", "value": "RoleBinding"}, 2 | {"op": "add", "path": "/metadata/namespace", "value": "ack-system"}, 3 | {"op": "replace", "path": "/roleRef/kind", "value": "Role"}] -------------------------------------------------------------------------------- /config/overlays/namespaced/role.json: -------------------------------------------------------------------------------- 1 | [{"op": "replace", "path": "/kind", "value": "Role"}, 2 | {"op": "add", "path": "/metadata/namespace", "value": "ack-system"}] -------------------------------------------------------------------------------- /config/rbac/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: ack-ecr-controller-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: ack-ecr-controller 9 | subjects: 10 | - kind: ServiceAccount 11 | name: ack-ecr-controller 12 | namespace: ack-system 13 | -------------------------------------------------------------------------------- /config/rbac/cluster-role-controller.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: ack-ecr-controller 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | - secrets 12 | verbs: 13 | - get 14 | - list 15 | - patch 16 | - watch 17 | - apiGroups: 18 | - "" 19 | resources: 20 | - namespaces 21 | verbs: 22 | - get 23 | - list 24 | - watch 25 | - apiGroups: 26 | - ecr.services.k8s.aws 27 | resources: 28 | - pullthroughcacherules 29 | - repositories 30 | verbs: 31 | - create 32 | - delete 33 | - get 34 | - list 35 | - patch 36 | - update 37 | - watch 38 | - apiGroups: 39 | - ecr.services.k8s.aws 40 | resources: 41 | - pullthroughcacherules/status 42 | - repositories/status 43 | verbs: 44 | - get 45 | - patch 46 | - update 47 | - apiGroups: 48 | - services.k8s.aws 49 | resources: 50 | - adoptedresources 51 | - fieldexports 52 | verbs: 53 | - create 54 | - delete 55 | - get 56 | - list 57 | - patch 58 | - update 59 | - watch 60 | - apiGroups: 61 | - services.k8s.aws 62 | resources: 63 | - adoptedresources/status 64 | - fieldexports/status 65 | verbs: 66 | - get 67 | - patch 68 | - update 69 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - cluster-role-binding.yaml 3 | - cluster-role-controller.yaml 4 | - role-reader.yaml 5 | - role-writer.yaml 6 | - service-account.yaml 7 | - leader-election-role.yaml 8 | - leader-election-role-binding.yaml 9 | -------------------------------------------------------------------------------- /config/rbac/leader-election-role-binding.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: RoleBinding 4 | metadata: 5 | namespace: ack-system 6 | name: ecr-leader-election-rolebinding 7 | roleRef: 8 | apiGroup: rbac.authorization.k8s.io 9 | kind: Role 10 | name: ecr-leader-election-role 11 | subjects: 12 | - kind: ServiceAccount 13 | name: ack-ecr-controller 14 | namespace: ack-system 15 | -------------------------------------------------------------------------------- /config/rbac/leader-election-role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: ecr-leader-election-role 6 | namespace: ack-system 7 | rules: 8 | - apiGroups: 9 | - coordination.k8s.io 10 | resources: 11 | - leases 12 | verbs: 13 | - get 14 | - list 15 | - watch 16 | - create 17 | - update 18 | - patch 19 | - delete 20 | - apiGroups: 21 | - "" 22 | resources: 23 | - events 24 | verbs: 25 | - create 26 | - patch 27 | -------------------------------------------------------------------------------- /config/rbac/role-reader.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | creationTimestamp: null 6 | name: ack-ecr-reader 7 | namespace: default 8 | rules: 9 | - apiGroups: 10 | - ecr.services.k8s.aws 11 | resources: 12 | - pullthroughcacherules 13 | - repositories 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | -------------------------------------------------------------------------------- /config/rbac/role-writer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | creationTimestamp: null 6 | name: ack-ecr-writer 7 | namespace: default 8 | rules: 9 | - apiGroups: 10 | - ecr.services.k8s.aws 11 | resources: 12 | - pullthroughcacherules 13 | - repositories 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - ecr.services.k8s.aws 24 | resources: 25 | - pullthroughcacherules 26 | - repositories 27 | verbs: 28 | - get 29 | - patch 30 | - update 31 | -------------------------------------------------------------------------------- /config/rbac/service-account.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: ack-ecr-controller 6 | namespace: ack-system 7 | -------------------------------------------------------------------------------- /generator.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | Repository: 3 | print: 4 | order_by: index 5 | fields: 6 | Name: 7 | is_primary_key: true 8 | is_required: true 9 | from: 10 | operation: CreateRepository 11 | path: RepositoryName 12 | LifecyclePolicy: 13 | from: 14 | operation: PutLifecyclePolicy 15 | path: LifecyclePolicyText 16 | Policy: 17 | from: 18 | operation: SetRepositoryPolicy 19 | path: PolicyText 20 | Tags: 21 | compare: 22 | is_ignored: true 23 | RegistryID: 24 | late_initialize: {} 25 | print: 26 | name: REGISTRY-ID 27 | index: 0 28 | ImageTagMutability: 29 | print: 30 | name: IMAGE-TAG-MUTABILITY 31 | index: 1 32 | renames: 33 | operations: 34 | CreateRepository: 35 | input_fields: 36 | RepositoryName: Name 37 | DeleteRepository: 38 | input_fields: 39 | RepositoryName: Name 40 | DescribeRepositories: 41 | input_fields: 42 | RepositoryName: Name 43 | exceptions: 44 | errors: 45 | 404: 46 | code: RepositoryNotFoundException 47 | list_operation: 48 | match_fields: 49 | - Name 50 | hooks: 51 | delta_pre_compare: 52 | code: customPreCompare(delta, a, b) 53 | sdk_read_many_post_set_output: 54 | template_path: hooks/repository/sdk_read_many_post_set_output.go.tpl 55 | sdk_create_post_set_output: 56 | template_path: hooks/repository/sdk_create_post_set_output.go.tpl 57 | sdk_delete_post_build_request: 58 | template_path: hooks/repository/sdk_delete_post_build_request.go.tpl 59 | update_operation: 60 | custom_method_name: customUpdateRepository 61 | PullThroughCacheRule: 62 | exceptions: 63 | errors: 64 | 404: 65 | code: PullThroughCacheRuleNotFoundException 66 | fields: 67 | ECRRepositoryPrefix: 68 | is_primary_key: true 69 | is_immutable: true 70 | RegistryID: 71 | is_immutable: true 72 | UpstreamRegistryURL: 73 | is_immutable: true 74 | hooks: 75 | sdk_read_many_post_build_request: 76 | template_path: hooks/pull_through_cache_rule/sdk_read_many_post_build_request.go.tpl 77 | list_operation: 78 | match_fields: 79 | - RegistryId 80 | - EcrRepositoryPrefix 81 | tags: 82 | ignore: true 83 | ignore: 84 | resource_names: 85 | - RepositoryCreationTemplate 86 | field_paths: 87 | - CreatePullThroughCacheRuleOutput.CredentialArn 88 | - CreatePullThroughCacheRuleOutput.UpstreamRegistry 89 | - CreatePullThroughCacheRuleInput.UpstreamRegistry 90 | - CreatePullThroughCacheRuleInput.CredentialArn -------------------------------------------------------------------------------- /go.local.mod: -------------------------------------------------------------------------------- 1 | module github.com/aws-controllers-k8s/ecr-controller 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/aws-controllers-k8s/runtime v0.15.2 7 | github.com/aws/aws-sdk-go v1.42.0 8 | github.com/go-logr/logr v0.1.0 9 | github.com/spf13/pflag v1.0.5 10 | k8s.io/api v0.18.2 11 | k8s.io/apimachinery v0.18.6 12 | k8s.io/client-go v0.18.2 13 | sigs.k8s.io/controller-runtime v0.6.0 14 | ) 15 | 16 | replace ( 17 | github.com/aws-controllers-k8s/runtime => ../runtime 18 | ) 19 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/aws-controllers-k8s/ecr-controller 2 | 3 | go 1.24.0 4 | 5 | toolchain go1.24.1 6 | 7 | require ( 8 | github.com/aws-controllers-k8s/runtime v0.47.0 9 | github.com/aws/aws-sdk-go v1.49.0 10 | github.com/aws/aws-sdk-go-v2 v1.33.0 11 | github.com/aws/aws-sdk-go-v2/service/ecr v1.38.6 12 | github.com/aws/smithy-go v1.22.2 13 | github.com/go-logr/logr v1.4.2 14 | github.com/spf13/pflag v1.0.5 15 | github.com/stretchr/testify v1.9.0 16 | k8s.io/api v0.32.1 17 | k8s.io/apimachinery v0.32.1 18 | k8s.io/client-go v0.32.1 19 | sigs.k8s.io/controller-runtime v0.20.4 20 | ) 21 | 22 | require ( 23 | github.com/aws/aws-sdk-go-v2/config v1.28.6 // indirect 24 | github.com/aws/aws-sdk-go-v2/credentials v1.17.47 // indirect 25 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect 26 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 // indirect 27 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 // indirect 28 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect 29 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect 30 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect 31 | github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect 32 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect 33 | github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 // indirect 34 | github.com/beorn7/perks v1.0.1 // indirect 35 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 36 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 37 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 38 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 39 | github.com/evanphx/json-patch/v5 v5.9.11 // indirect 40 | github.com/fsnotify/fsnotify v1.7.0 // indirect 41 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 42 | github.com/go-logr/zapr v1.3.0 // indirect 43 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 44 | github.com/go-openapi/jsonreference v0.20.2 // indirect 45 | github.com/go-openapi/swag v0.23.0 // indirect 46 | github.com/gogo/protobuf v1.3.2 // indirect 47 | github.com/golang/protobuf v1.5.4 // indirect 48 | github.com/google/btree v1.1.3 // indirect 49 | github.com/google/gnostic-models v0.6.8 // indirect 50 | github.com/google/go-cmp v0.6.0 // indirect 51 | github.com/google/gofuzz v1.2.0 // indirect 52 | github.com/google/uuid v1.6.0 // indirect 53 | github.com/itchyny/gojq v0.12.6 // indirect 54 | github.com/itchyny/timefmt-go v0.1.3 // indirect 55 | github.com/jaypipes/envutil v1.0.0 // indirect 56 | github.com/jmespath/go-jmespath v0.4.0 // indirect 57 | github.com/josharian/intern v1.0.0 // indirect 58 | github.com/json-iterator/go v1.1.12 // indirect 59 | github.com/mailru/easyjson v0.7.7 // indirect 60 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 61 | github.com/modern-go/reflect2 v1.0.2 // indirect 62 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 63 | github.com/pkg/errors v0.9.1 // indirect 64 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 65 | github.com/prometheus/client_golang v1.19.1 // indirect 66 | github.com/prometheus/client_model v0.6.1 // indirect 67 | github.com/prometheus/common v0.55.0 // indirect 68 | github.com/prometheus/procfs v0.15.1 // indirect 69 | github.com/samber/lo v1.37.0 // indirect 70 | github.com/x448/float16 v0.8.4 // indirect 71 | go.uber.org/multierr v1.11.0 // indirect 72 | go.uber.org/zap v1.27.0 // indirect 73 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect 74 | golang.org/x/net v0.37.0 // indirect 75 | golang.org/x/oauth2 v0.23.0 // indirect 76 | golang.org/x/sync v0.12.0 // indirect 77 | golang.org/x/sys v0.31.0 // indirect 78 | golang.org/x/term v0.30.0 // indirect 79 | golang.org/x/text v0.23.0 // indirect 80 | golang.org/x/time v0.7.0 // indirect 81 | gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect 82 | google.golang.org/protobuf v1.35.1 // indirect 83 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 84 | gopkg.in/inf.v0 v0.9.1 // indirect 85 | gopkg.in/yaml.v3 v3.0.1 // indirect 86 | k8s.io/apiextensions-apiserver v0.32.1 // indirect 87 | k8s.io/klog/v2 v2.130.1 // indirect 88 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect 89 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect 90 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect 91 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect 92 | sigs.k8s.io/yaml v1.4.0 // indirect 93 | ) 94 | -------------------------------------------------------------------------------- /helm/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | name: ecr-chart 3 | description: A Helm chart for the ACK service controller for Amazon Elastic Container Registry (ECR) 4 | version: 1.0.30 5 | appVersion: 1.0.30 6 | home: https://github.com/aws-controllers-k8s/ecr-controller 7 | icon: https://raw.githubusercontent.com/aws/eks-charts/master/docs/logo/aws.png 8 | sources: 9 | - https://github.com/aws-controllers-k8s/ecr-controller 10 | maintainers: 11 | - name: ACK Admins 12 | url: https://github.com/orgs/aws-controllers-k8s/teams/ack-admin 13 | - name: ECR Admins 14 | url: https://github.com/orgs/aws-controllers-k8s/teams/ecr-maintainer 15 | keywords: 16 | - aws 17 | - kubernetes 18 | - ecr 19 | -------------------------------------------------------------------------------- /helm/crds/ecr.services.k8s.aws_pullthroughcacherules.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.16.2 7 | name: pullthroughcacherules.ecr.services.k8s.aws 8 | spec: 9 | group: ecr.services.k8s.aws 10 | names: 11 | kind: PullThroughCacheRule 12 | listKind: PullThroughCacheRuleList 13 | plural: pullthroughcacherules 14 | singular: pullthroughcacherule 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: PullThroughCacheRule is the Schema for the PullThroughCacheRules 21 | API 22 | properties: 23 | apiVersion: 24 | description: |- 25 | APIVersion defines the versioned schema of this representation of an object. 26 | Servers should convert recognized schemas to the latest internal value, and 27 | may reject unrecognized values. 28 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 29 | type: string 30 | kind: 31 | description: |- 32 | Kind is a string value representing the REST resource this object represents. 33 | Servers may infer this from the endpoint the client submits requests to. 34 | Cannot be updated. 35 | In CamelCase. 36 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 37 | type: string 38 | metadata: 39 | type: object 40 | spec: 41 | description: |- 42 | PullThroughCacheRuleSpec defines the desired state of PullThroughCacheRule. 43 | 44 | The details of a pull through cache rule. 45 | properties: 46 | ecrRepositoryPrefix: 47 | description: |- 48 | The repository name prefix to use when caching images from the source registry. 49 | 50 | Regex Pattern: `^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$` 51 | type: string 52 | x-kubernetes-validations: 53 | - message: Value is immutable once set 54 | rule: self == oldSelf 55 | registryID: 56 | description: |- 57 | The Amazon Web Services account ID associated with the registry to create 58 | the pull through cache rule for. If you do not specify a registry, the default 59 | registry is assumed. 60 | 61 | Regex Pattern: `^[0-9]{12}$` 62 | type: string 63 | x-kubernetes-validations: 64 | - message: Value is immutable once set 65 | rule: self == oldSelf 66 | upstreamRegistryURL: 67 | description: |- 68 | The registry URL of the upstream public registry to use as the source for 69 | the pull through cache rule. The following is the syntax to use for each 70 | supported upstream registry. 71 | 72 | - Amazon ECR Public (ecr-public) - public.ecr.aws 73 | 74 | - Docker Hub (docker-hub) - registry-1.docker.io 75 | 76 | - Quay (quay) - quay.io 77 | 78 | - Kubernetes (k8s) - registry.k8s.io 79 | 80 | - GitHub Container Registry (github-container-registry) - ghcr.io 81 | 82 | - Microsoft Azure Container Registry (azure-container-registry) - .azurecr.io 83 | type: string 84 | x-kubernetes-validations: 85 | - message: Value is immutable once set 86 | rule: self == oldSelf 87 | required: 88 | - ecrRepositoryPrefix 89 | - upstreamRegistryURL 90 | type: object 91 | status: 92 | description: PullThroughCacheRuleStatus defines the observed state of 93 | PullThroughCacheRule 94 | properties: 95 | ackResourceMetadata: 96 | description: |- 97 | All CRs managed by ACK have a common `Status.ACKResourceMetadata` member 98 | that is used to contain resource sync state, account ownership, 99 | constructed ARN for the resource 100 | properties: 101 | arn: 102 | description: |- 103 | ARN is the Amazon Resource Name for the resource. This is a 104 | globally-unique identifier and is set only by the ACK service controller 105 | once the controller has orchestrated the creation of the resource OR 106 | when it has verified that an "adopted" resource (a resource where the 107 | ARN annotation was set by the Kubernetes user on the CR) exists and 108 | matches the supplied CR's Spec field values. 109 | https://github.com/aws/aws-controllers-k8s/issues/270 110 | type: string 111 | ownerAccountID: 112 | description: |- 113 | OwnerAccountID is the AWS Account ID of the account that owns the 114 | backend AWS service API resource. 115 | type: string 116 | region: 117 | description: Region is the AWS region in which the resource exists 118 | or will exist. 119 | type: string 120 | required: 121 | - ownerAccountID 122 | - region 123 | type: object 124 | conditions: 125 | description: |- 126 | All CRs managed by ACK have a common `Status.Conditions` member that 127 | contains a collection of `ackv1alpha1.Condition` objects that describe 128 | the various terminal states of the CR and its backend AWS service API 129 | resource 130 | items: 131 | description: |- 132 | Condition is the common struct used by all CRDs managed by ACK service 133 | controllers to indicate terminal states of the CR and its backend AWS 134 | service API resource 135 | properties: 136 | lastTransitionTime: 137 | description: Last time the condition transitioned from one status 138 | to another. 139 | format: date-time 140 | type: string 141 | message: 142 | description: A human readable message indicating details about 143 | the transition. 144 | type: string 145 | reason: 146 | description: The reason for the condition's last transition. 147 | type: string 148 | status: 149 | description: Status of the condition, one of True, False, Unknown. 150 | type: string 151 | type: 152 | description: Type is the type of the Condition 153 | type: string 154 | required: 155 | - status 156 | - type 157 | type: object 158 | type: array 159 | createdAt: 160 | description: |- 161 | The date and time, in JavaScript date format, when the pull through cache 162 | rule was created. 163 | format: date-time 164 | type: string 165 | type: object 166 | type: object 167 | served: true 168 | storage: true 169 | subresources: 170 | status: {} 171 | -------------------------------------------------------------------------------- /helm/crds/services.k8s.aws_fieldexports.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.16.2 7 | name: fieldexports.services.k8s.aws 8 | spec: 9 | group: services.k8s.aws 10 | names: 11 | kind: FieldExport 12 | listKind: FieldExportList 13 | plural: fieldexports 14 | singular: fieldexport 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: FieldExport is the schema for the FieldExport API. 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: FieldExportSpec defines the desired state of the FieldExport. 41 | properties: 42 | from: 43 | description: |- 44 | ResourceFieldSelector provides the values necessary to identify an individual 45 | field on an individual K8s resource. 46 | properties: 47 | path: 48 | type: string 49 | resource: 50 | description: |- 51 | NamespacedResource provides all the values necessary to identify an ACK 52 | resource of a given type (within the same namespace as the custom resource 53 | containing this type). 54 | properties: 55 | group: 56 | type: string 57 | kind: 58 | type: string 59 | name: 60 | type: string 61 | required: 62 | - group 63 | - kind 64 | - name 65 | type: object 66 | required: 67 | - path 68 | - resource 69 | type: object 70 | to: 71 | description: |- 72 | FieldExportTarget provides the values necessary to identify the 73 | output path for a field export. 74 | properties: 75 | key: 76 | description: Key overrides the default value (`.`) 77 | for the FieldExport target 78 | type: string 79 | kind: 80 | description: |- 81 | FieldExportOutputType represents all types that can be produced by a field 82 | export operation 83 | enum: 84 | - configmap 85 | - secret 86 | type: string 87 | name: 88 | type: string 89 | namespace: 90 | description: Namespace is marked as optional, so we cannot compose 91 | `NamespacedName` 92 | type: string 93 | required: 94 | - kind 95 | - name 96 | type: object 97 | required: 98 | - from 99 | - to 100 | type: object 101 | status: 102 | description: FieldExportStatus defines the observed status of the FieldExport. 103 | properties: 104 | conditions: 105 | description: |- 106 | A collection of `ackv1alpha1.Condition` objects that describe the various 107 | recoverable states of the field CR 108 | items: 109 | description: |- 110 | Condition is the common struct used by all CRDs managed by ACK service 111 | controllers to indicate terminal states of the CR and its backend AWS 112 | service API resource 113 | properties: 114 | lastTransitionTime: 115 | description: Last time the condition transitioned from one status 116 | to another. 117 | format: date-time 118 | type: string 119 | message: 120 | description: A human readable message indicating details about 121 | the transition. 122 | type: string 123 | reason: 124 | description: The reason for the condition's last transition. 125 | type: string 126 | status: 127 | description: Status of the condition, one of True, False, Unknown. 128 | type: string 129 | type: 130 | description: Type is the type of the Condition 131 | type: string 132 | required: 133 | - status 134 | - type 135 | type: object 136 | type: array 137 | required: 138 | - conditions 139 | type: object 140 | type: object 141 | served: true 142 | storage: true 143 | subresources: 144 | status: {} 145 | -------------------------------------------------------------------------------- /helm/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | {{ .Chart.Name }} has been installed. 2 | This chart deploys "public.ecr.aws/aws-controllers-k8s/ecr-controller:1.0.30". 3 | 4 | Check its status by running: 5 | kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/instance={{ .Release.Name }}" 6 | 7 | You are now able to create Amazon Elastic Container Registry (ECR) resources! 8 | 9 | The controller is running in "{{ .Values.installScope }}" mode. 10 | The controller is configured to manage AWS resources in region: "{{ .Values.aws.region }}" 11 | 12 | Visit https://aws-controllers-k8s.github.io/community/reference/ for an API 13 | reference of all the resources that can be created using this controller. 14 | 15 | For more information on the AWS Controllers for Kubernetes (ACK) project, visit: 16 | https://aws-controllers-k8s.github.io/community/ 17 | -------------------------------------------------------------------------------- /helm/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* The name of the application this chart installs */}} 2 | {{- define "ack-ecr-controller.app.name" -}} 3 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 4 | {{- end -}} 5 | 6 | {{/* 7 | Create a default fully qualified app name. 8 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 9 | If release name contains chart name it will be used as a full name. 10 | */}} 11 | {{- define "ack-ecr-controller.app.fullname" -}} 12 | {{- if .Values.fullnameOverride -}} 13 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 14 | {{- else -}} 15 | {{- $name := default .Chart.Name .Values.nameOverride -}} 16 | {{- if contains $name .Release.Name -}} 17 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 18 | {{- else -}} 19 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 20 | {{- end -}} 21 | {{- end -}} 22 | {{- end -}} 23 | 24 | {{/* The name and version as used by the chart label */}} 25 | {{- define "ack-ecr-controller.chart.name-version" -}} 26 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 27 | {{- end -}} 28 | 29 | {{/* The name of the service account to use */}} 30 | {{- define "ack-ecr-controller.service-account.name" -}} 31 | {{ default "default" .Values.serviceAccount.name }} 32 | {{- end -}} 33 | 34 | {{- define "ack-ecr-controller.watch-namespace" -}} 35 | {{- if eq .Values.installScope "namespace" -}} 36 | {{ .Values.watchNamespace | default .Release.Namespace }} 37 | {{- end -}} 38 | {{- end -}} 39 | 40 | {{/* The mount path for the shared credentials file */}} 41 | {{- define "ack-ecr-controller.aws.credentials.secret_mount_path" -}} 42 | {{- "/var/run/secrets/aws" -}} 43 | {{- end -}} 44 | 45 | {{/* The path the shared credentials file is mounted */}} 46 | {{- define "ack-ecr-controller.aws.credentials.path" -}} 47 | {{ $secret_mount_path := include "ack-ecr-controller.aws.credentials.secret_mount_path" . }} 48 | {{- printf "%s/%s" $secret_mount_path .Values.aws.credentials.secretKey -}} 49 | {{- end -}} 50 | 51 | {{/* The rules a of ClusterRole or Role */}} 52 | {{- define "ack-ecr-controller.rbac-rules" -}} 53 | rules: 54 | - apiGroups: 55 | - "" 56 | resources: 57 | - configmaps 58 | - secrets 59 | verbs: 60 | - get 61 | - list 62 | - patch 63 | - watch 64 | - apiGroups: 65 | - "" 66 | resources: 67 | - namespaces 68 | verbs: 69 | - get 70 | - list 71 | - watch 72 | - apiGroups: 73 | - ecr.services.k8s.aws 74 | resources: 75 | - pullthroughcacherules 76 | - repositories 77 | verbs: 78 | - create 79 | - delete 80 | - get 81 | - list 82 | - patch 83 | - update 84 | - watch 85 | - apiGroups: 86 | - ecr.services.k8s.aws 87 | resources: 88 | - pullthroughcacherules/status 89 | - repositories/status 90 | verbs: 91 | - get 92 | - patch 93 | - update 94 | - apiGroups: 95 | - services.k8s.aws 96 | resources: 97 | - adoptedresources 98 | - fieldexports 99 | verbs: 100 | - create 101 | - delete 102 | - get 103 | - list 104 | - patch 105 | - update 106 | - watch 107 | - apiGroups: 108 | - services.k8s.aws 109 | resources: 110 | - adoptedresources/status 111 | - fieldexports/status 112 | verbs: 113 | - get 114 | - patch 115 | - update 116 | {{- end }} 117 | 118 | {{/* Convert k/v map to string like: "key1=value1,key2=value2,..." */}} 119 | {{- define "ack-ecr-controller.feature-gates" -}} 120 | {{- $list := list -}} 121 | {{- range $k, $v := .Values.featureGates -}} 122 | {{- $list = append $list (printf "%s=%s" $k ( $v | toString)) -}} 123 | {{- end -}} 124 | {{ join "," $list }} 125 | {{- end -}} 126 | -------------------------------------------------------------------------------- /helm/templates/caches-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: {{ include "ack-ecr-controller.app.fullname" . }}-namespace-caches 5 | labels: 6 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 7 | app.kubernetes.io/instance: {{ .Release.Name }} 8 | app.kubernetes.io/managed-by: Helm 9 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 10 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 11 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 12 | roleRef: 13 | kind: ClusterRole 14 | apiGroup: rbac.authorization.k8s.io 15 | name: {{ include "ack-ecr-controller.app.fullname" . }}-namespace-caches 16 | subjects: 17 | - kind: ServiceAccount 18 | name: {{ include "ack-ecr-controller.service-account.name" . }} 19 | namespace: {{ .Release.Namespace }} 20 | --- 21 | apiVersion: rbac.authorization.k8s.io/v1 22 | kind: RoleBinding 23 | metadata: 24 | name: {{ include "ack-ecr-controller.app.fullname" . }}-configmaps-cache 25 | namespace: {{ .Release.Namespace }} 26 | labels: 27 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 28 | app.kubernetes.io/instance: {{ .Release.Name }} 29 | app.kubernetes.io/managed-by: Helm 30 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 31 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 32 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 33 | roleRef: 34 | kind: Role 35 | apiGroup: rbac.authorization.k8s.io 36 | name: {{ include "ack-ecr-controller.app.fullname" . }}-configmaps-cache 37 | subjects: 38 | - kind: ServiceAccount 39 | name: {{ include "ack-ecr-controller.service-account.name" . }} 40 | namespace: {{ .Release.Namespace }} 41 | -------------------------------------------------------------------------------- /helm/templates/caches-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: {{ include "ack-ecr-controller.app.fullname" . }}-namespaces-cache 5 | labels: 6 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 7 | app.kubernetes.io/instance: {{ .Release.Name }} 8 | app.kubernetes.io/managed-by: Helm 9 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 10 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 11 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 12 | rules: 13 | - apiGroups: 14 | - "" 15 | resources: 16 | - namespaces 17 | verbs: 18 | - get 19 | - list 20 | - watch 21 | --- 22 | apiVersion: rbac.authorization.k8s.io/v1 23 | kind: Role 24 | metadata: 25 | name: {{ include "ack-ecr-controller.app.fullname" . }}-configmaps-cache 26 | namespace: {{ .Release.Namespace }} 27 | labels: 28 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 29 | app.kubernetes.io/instance: {{ .Release.Name }} 30 | app.kubernetes.io/managed-by: Helm 31 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 32 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 33 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 34 | rules: 35 | - apiGroups: 36 | - "" 37 | resources: 38 | - configmaps 39 | verbs: 40 | - get 41 | - list 42 | - watch -------------------------------------------------------------------------------- /helm/templates/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | {{ if eq .Values.installScope "cluster" }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: {{ include "ack-ecr-controller.app.fullname" . }}-rolebinding 6 | labels: 7 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 8 | app.kubernetes.io/instance: {{ .Release.Name }} 9 | app.kubernetes.io/managed-by: Helm 10 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 11 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 12 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 13 | roleRef: 14 | kind: ClusterRole 15 | apiGroup: rbac.authorization.k8s.io 16 | name: {{ include "ack-ecr-controller.app.fullname" . }} 17 | subjects: 18 | - kind: ServiceAccount 19 | name: {{ include "ack-ecr-controller.service-account.name" . }} 20 | namespace: {{ .Release.Namespace }} 21 | {{ else if eq .Values.installScope "namespace" }} 22 | {{ $wn := include "ack-ecr-controller.watch-namespace" . }} 23 | {{ $namespaces := split "," $wn }} 24 | {{ $fullname := include "ack-ecr-controller.app.fullname" . }} 25 | {{ $releaseNamespace := .Release.Namespace }} 26 | {{ $serviceAccountName := include "ack-ecr-controller.service-account.name" . }} 27 | {{ $chartVersion := include "ack-ecr-controller.chart.name-version" . }} 28 | {{ $appVersion := .Chart.AppVersion | quote }} 29 | {{ range $namespaces }} 30 | --- 31 | apiVersion: rbac.authorization.k8s.io/v1 32 | kind: RoleBinding 33 | metadata: 34 | name: {{ $fullname }}-{{ . }} 35 | namespace: {{ . }} 36 | labels: 37 | app.kubernetes.io/name: {{ $fullname }} 38 | app.kubernetes.io/instance: {{ $.Release.Name }} 39 | app.kubernetes.io/managed-by: Helm 40 | app.kubernetes.io/version: {{ $appVersion }} 41 | k8s-app: {{ $fullname }} 42 | helm.sh/chart: {{ $chartVersion }} 43 | roleRef: 44 | kind: Role 45 | apiGroup: rbac.authorization.k8s.io 46 | name: {{ $fullname }}-{{ . }} 47 | subjects: 48 | - kind: ServiceAccount 49 | name: {{ $serviceAccountName }} 50 | namespace: {{ $releaseNamespace }} 51 | {{ end }} 52 | {{ end }} -------------------------------------------------------------------------------- /helm/templates/cluster-role-controller.yaml: -------------------------------------------------------------------------------- 1 | {{ $labels := .Values.role.labels }} 2 | {{ $appVersion := .Chart.AppVersion | quote }} 3 | {{ $rbacRules := include "ack-ecr-controller.rbac-rules" . }} 4 | {{ $fullname := include "ack-ecr-controller.app.fullname" . }} 5 | {{ $chartVersion := include "ack-ecr-controller.chart.name-version" . }} 6 | {{ if eq .Values.installScope "cluster" }} 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRole 9 | metadata: 10 | name: {{ include "ack-ecr-controller.app.fullname" . }} 11 | labels: 12 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 13 | app.kubernetes.io/instance: {{ .Release.Name }} 14 | app.kubernetes.io/managed-by: Helm 15 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 16 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 17 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 18 | {{- range $key, $value := $labels }} 19 | {{ $key }}: {{ $value | quote }} 20 | {{- end }} 21 | {{$rbacRules }} 22 | {{ else if eq .Values.installScope "namespace" }} 23 | {{ $wn := include "ack-ecr-controller.watch-namespace" . }} 24 | {{ $namespaces := split "," $wn }} 25 | {{ range $namespaces }} 26 | --- 27 | apiVersion: rbac.authorization.k8s.io/v1 28 | kind: Role 29 | metadata: 30 | name: {{ $fullname }}-{{ . }} 31 | namespace: {{ . }} 32 | labels: 33 | app.kubernetes.io/name: {{ $fullname }} 34 | app.kubernetes.io/instance: {{ $.Release.Name }} 35 | app.kubernetes.io/managed-by: Helm 36 | app.kubernetes.io/version: {{ $appVersion }} 37 | k8s-app: {{ $fullname }} 38 | helm.sh/chart: {{ $chartVersion }} 39 | {{- range $key, $value := $labels }} 40 | {{ $key }}: {{ $value | quote }} 41 | {{- end }} 42 | {{ $rbacRules }} 43 | {{ end }} 44 | {{ end }} -------------------------------------------------------------------------------- /helm/templates/leader-election-role-binding.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.leaderElection.enabled }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: RoleBinding 4 | metadata: 5 | name: {{ include "ack-ecr-controller.app.fullname" . }}-leaderelection 6 | {{ if .Values.leaderElection.namespace }} 7 | namespace: {{ .Values.leaderElection.namespace }} 8 | {{ else }} 9 | namespace: {{ .Release.Namespace }} 10 | {{ end }} 11 | labels: 12 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 13 | app.kubernetes.io/instance: {{ .Release.Name }} 14 | app.kubernetes.io/managed-by: Helm 15 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 16 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 17 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 18 | roleRef: 19 | apiGroup: rbac.authorization.k8s.io 20 | kind: Role 21 | name: {{ include "ack-ecr-controller.app.fullname" . }}-leaderelection 22 | subjects: 23 | - kind: ServiceAccount 24 | name: {{ include "ack-ecr-controller.service-account.name" . }} 25 | namespace: {{ .Release.Namespace }}{{- end }} 26 | -------------------------------------------------------------------------------- /helm/templates/leader-election-role.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.leaderElection.enabled }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: {{ include "ack-ecr-controller.app.fullname" . }}-leaderelection 6 | {{ if .Values.leaderElection.namespace }} 7 | namespace: {{ .Values.leaderElection.namespace }} 8 | {{ else }} 9 | namespace: {{ .Release.Namespace }} 10 | {{ end }} 11 | labels: 12 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 13 | app.kubernetes.io/instance: {{ .Release.Name }} 14 | app.kubernetes.io/managed-by: Helm 15 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 16 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 17 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 18 | rules: 19 | - apiGroups: 20 | - coordination.k8s.io 21 | resources: 22 | - leases 23 | verbs: 24 | - get 25 | - list 26 | - watch 27 | - create 28 | - update 29 | - patch 30 | - delete 31 | - apiGroups: 32 | - "" 33 | resources: 34 | - events 35 | verbs: 36 | - create 37 | - patch{{- end }} 38 | -------------------------------------------------------------------------------- /helm/templates/metrics-service.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.metrics.service.create }} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ .Chart.Name | trimSuffix "-chart" | trunc 44 }}-controller-metrics 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 9 | app.kubernetes.io/instance: {{ .Release.Name }} 10 | app.kubernetes.io/managed-by: Helm 11 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 12 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 13 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 14 | spec: 15 | selector: 16 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 17 | app.kubernetes.io/instance: {{ .Release.Name }} 18 | app.kubernetes.io/managed-by: Helm 19 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 20 | {{- range $key, $value := .Values.deployment.labels }} 21 | {{ $key }}: {{ $value | quote }} 22 | {{- end }} 23 | type: {{ .Values.metrics.service.type }} 24 | ports: 25 | - name: metricsport 26 | port: 8080 27 | targetPort: http 28 | protocol: TCP 29 | {{- end }} 30 | -------------------------------------------------------------------------------- /helm/templates/role-reader.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | creationTimestamp: null 6 | name: {{ include "ack-ecr-controller.app.fullname" . }}-reader 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 10 | app.kubernetes.io/instance: {{ .Release.Name }} 11 | app.kubernetes.io/managed-by: Helm 12 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 13 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 14 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 15 | rules: 16 | - apiGroups: 17 | - ecr.services.k8s.aws 18 | resources: 19 | - pullthroughcacherules 20 | - repositories 21 | verbs: 22 | - get 23 | - list 24 | - watch 25 | -------------------------------------------------------------------------------- /helm/templates/role-writer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | creationTimestamp: null 6 | name: {{ include "ack-ecr-controller.app.fullname" . }}-writer 7 | namespace: {{ .Release.Namespace }} 8 | labels: 9 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 10 | app.kubernetes.io/instance: {{ .Release.Name }} 11 | app.kubernetes.io/managed-by: Helm 12 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 13 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 14 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 15 | rules: 16 | - apiGroups: 17 | - ecr.services.k8s.aws 18 | resources: 19 | - pullthroughcacherules 20 | - repositories 21 | verbs: 22 | - create 23 | - delete 24 | - get 25 | - list 26 | - patch 27 | - update 28 | - watch 29 | - apiGroups: 30 | - ecr.services.k8s.aws 31 | resources: 32 | - pullthroughcacherules 33 | - repositories 34 | verbs: 35 | - get 36 | - patch 37 | - update 38 | -------------------------------------------------------------------------------- /helm/templates/service-account.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: {{ include "ack-ecr-controller.app.name" . }} 7 | app.kubernetes.io/instance: {{ .Release.Name }} 8 | app.kubernetes.io/managed-by: Helm 9 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 10 | k8s-app: {{ include "ack-ecr-controller.app.name" . }} 11 | helm.sh/chart: {{ include "ack-ecr-controller.chart.name-version" . }} 12 | name: {{ include "ack-ecr-controller.service-account.name" . }} 13 | namespace: {{ .Release.Namespace }} 14 | annotations: 15 | {{- range $key, $value := .Values.serviceAccount.annotations }} 16 | {{ $key }}: {{ $value | quote }} 17 | {{- end }} 18 | {{- end }} 19 | -------------------------------------------------------------------------------- /helm/values.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft-07/schema#", 3 | "properties": { 4 | "image": { 5 | "description": "Container Image", 6 | "properties": { 7 | "repository": { 8 | "type": "string", 9 | "minLength": 1 10 | }, 11 | "tag": { 12 | "type": "string", 13 | "minLength": 1 14 | }, 15 | "pullPolicy": { 16 | "type": "string", 17 | "enum": ["IfNotPresent", "Always", "Never"] 18 | }, 19 | "pullSecrets": { 20 | "type": "array" 21 | } 22 | }, 23 | "required": [ 24 | "repository", 25 | "tag", 26 | "pullPolicy" 27 | ], 28 | "type": "object" 29 | }, 30 | "nameOverride": { 31 | "type": "string" 32 | }, 33 | "fullNameOverride": { 34 | "type": "string" 35 | }, 36 | "deployment": { 37 | "description": "Deployment settings", 38 | "properties": { 39 | "annotations": { 40 | "type": "object" 41 | }, 42 | "labels": { 43 | "type": "object" 44 | }, 45 | "containerPort": { 46 | "type": "integer", 47 | "minimum": 1, 48 | "maximum": 65535 49 | }, 50 | "replicas": { 51 | "type": "integer" 52 | }, 53 | "nodeSelector": { 54 | "type": "object" 55 | }, 56 | "tolerations": { 57 | "type": "array" 58 | }, 59 | "affinity": { 60 | "type": "object" 61 | }, 62 | "priorityClassName": { 63 | "type": "string" 64 | }, 65 | "extraVolumeMounts": { 66 | "type": "array" 67 | }, 68 | "extraVolumes": { 69 | "type": "array" 70 | }, 71 | "extraEnvVars": { 72 | "type": "array" 73 | } 74 | }, 75 | "required": [ 76 | "containerPort" 77 | ], 78 | "type": "object" 79 | }, 80 | "role": { 81 | "description": "Role settings", 82 | "properties": { 83 | "labels": { 84 | "type": "object" 85 | } 86 | } 87 | }, 88 | "metrics": { 89 | "description": "Metrics settings", 90 | "properties": { 91 | "service": { 92 | "description": "Kubernetes service settings", 93 | "properties": { 94 | "create": { 95 | "type": "boolean" 96 | }, 97 | "type": { 98 | "type": "string", 99 | "enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"] 100 | } 101 | }, 102 | "required": [ 103 | "create", 104 | "type" 105 | ], 106 | "type": "object" 107 | } 108 | }, 109 | "required": [ 110 | "service" 111 | ], 112 | "type": "object" 113 | }, 114 | "resources": { 115 | "description": "Kubernetes resources settings", 116 | "properties": { 117 | "requests": { 118 | "description": "Kubernetes resource requests", 119 | "properties": { 120 | "memory": { 121 | "oneOf": [ 122 | { "type": "number" }, 123 | { "type": "string" } 124 | ] 125 | }, 126 | "cpu": { 127 | "oneOf": [ 128 | { "type": "number" }, 129 | { "type": "string" } 130 | ] 131 | } 132 | }, 133 | "required": [ 134 | "memory", 135 | "cpu" 136 | ], 137 | "type": "object" 138 | }, 139 | "limits": { 140 | "description": "Kubernetes resource limits", 141 | "properties": { 142 | "memory": { 143 | "oneOf": [ 144 | { "type": "number" }, 145 | { "type": "string" } 146 | ] 147 | }, 148 | "cpu": { 149 | "oneOf": [ 150 | { "type": "number" }, 151 | { "type": "string" } 152 | ] 153 | } 154 | }, 155 | "required": [ 156 | "memory", 157 | "cpu" 158 | ], 159 | "type": "object" 160 | } 161 | }, 162 | "required": [ 163 | "requests", 164 | "limits" 165 | ], 166 | "type": "object" 167 | }, 168 | "aws": { 169 | "description": "AWS API settings", 170 | "properties": { 171 | "region": { 172 | "type": "string" 173 | }, 174 | "endpoint": { 175 | "type": "string" 176 | }, 177 | "credentials": { 178 | "description": "AWS credentials information", 179 | "properties": { 180 | "secretName": { 181 | "type": "string" 182 | }, 183 | "secretKey": { 184 | "type": "string" 185 | }, 186 | "profile": { 187 | "type": "string" 188 | } 189 | }, 190 | "type": "object" 191 | } 192 | }, 193 | "type": "object" 194 | }, 195 | "log": { 196 | "description": "Logging settings", 197 | "properties": { 198 | "enable_development_logging": { 199 | "type": "boolean" 200 | }, 201 | "level": { 202 | "type": "string" 203 | } 204 | }, 205 | "type": "object" 206 | }, 207 | "installScope": { 208 | "type": "string", 209 | "enum": ["cluster", "namespace"] 210 | }, 211 | "watchNamespace": { 212 | "type": "string" 213 | }, 214 | "watchSelectors": { 215 | "type": "string" 216 | }, 217 | "resourceTags": { 218 | "type": "array", 219 | "items": { 220 | "type": "string", 221 | "pattern": "(^$|^.*=.*$)" 222 | } 223 | }, 224 | "deletionPolicy": { 225 | "type": "string", 226 | "enum": ["delete", "retain"] 227 | }, 228 | "reconcile": { 229 | "description": "Reconcile settings. This is used to configure the controller's reconciliation behavior. e.g resyncPeriod and maxConcurrentSyncs", 230 | "properties": { 231 | "defaultResyncPeriod": { 232 | "type": "number" 233 | }, 234 | "resourceResyncPeriods": { 235 | "type": "object" 236 | }, 237 | "defaultMaxConcurentSyncs": { 238 | "type": "number" 239 | }, 240 | "resourceMaxConcurrentSyncs": { 241 | "type": "object" 242 | }, 243 | "resources": { 244 | "type": "array", 245 | "items": { 246 | "type": "string" 247 | }, 248 | "description": "List of resource kinds to reconcile. If empty, all resources will be reconciled.", 249 | "default": [] 250 | } 251 | }, 252 | "type": "object" 253 | }, 254 | "leaderElection": { 255 | "description": "Parameter to configure the controller's leader election system.", 256 | "properties": { 257 | "enabled": { 258 | "type": "boolean" 259 | }, 260 | "namespace": { 261 | "type": "string" 262 | } 263 | }, 264 | "type": "object" 265 | }, 266 | "serviceAccount": { 267 | "description": "ServiceAccount settings", 268 | "properties": { 269 | "create": { 270 | "type": "boolean" 271 | }, 272 | "name": { 273 | "type": "string" 274 | }, 275 | "annotations": { 276 | "type": "object" 277 | } 278 | }, 279 | "type": "object" 280 | } 281 | }, 282 | "featureGates": { 283 | "description": "Feature gates settings", 284 | "type": "object", 285 | "additionalProperties": { 286 | "type": "boolean" 287 | } 288 | }, 289 | "required": [ 290 | "image", 291 | "deployment", 292 | "metrics", 293 | "resources", 294 | "log", 295 | "installScope", 296 | "resourceTags", 297 | "serviceAccount" 298 | ], 299 | "title": "Values", 300 | "type": "object" 301 | } 302 | -------------------------------------------------------------------------------- /helm/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for ack-ecr-controller. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | image: 6 | repository: public.ecr.aws/aws-controllers-k8s/ecr-controller 7 | tag: 1.0.30 8 | pullPolicy: IfNotPresent 9 | pullSecrets: [] 10 | 11 | nameOverride: "" 12 | fullnameOverride: "" 13 | 14 | deployment: 15 | annotations: {} 16 | labels: {} 17 | containerPort: 8080 18 | # Number of Deployment replicas 19 | # This determines how many instances of the controller will be running. It's recommended 20 | # to enable leader election if you need to increase the number of replicas > 1 21 | replicas: 1 22 | # Which nodeSelector to set? 23 | # See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector 24 | nodeSelector: 25 | kubernetes.io/os: linux 26 | # Which tolerations to set? 27 | # See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ 28 | tolerations: [] 29 | # What affinity to set? 30 | # See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity 31 | affinity: {} 32 | # Which priorityClassName to set? 33 | # See: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#pod-priority 34 | priorityClassName: "" 35 | # Specifies the hostname of the Pod. 36 | # If not specified, the pod's hostname will be set to a system-defined value. 37 | hostNetwork: false 38 | # Set DNS policy for the pod. 39 | # Defaults to "ClusterFirst". 40 | # Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. 41 | # To have DNS options set along with hostNetwork, you have to specify DNS policy 42 | # explicitly to 'ClusterFirstWithHostNet'. 43 | dnsPolicy: ClusterFirst 44 | extraVolumes: [] 45 | extraVolumeMounts: [] 46 | 47 | # Additional server container environment variables 48 | # 49 | # You specify this manually like you would a raw deployment manifest. 50 | # This means you can bind in environment variables from secrets. 51 | # 52 | # e.g. static environment variable: 53 | # - name: DEMO_GREETING 54 | # value: "Hello from the environment" 55 | # 56 | # e.g. secret environment variable: 57 | # - name: USERNAME 58 | # valueFrom: 59 | # secretKeyRef: 60 | # name: mysecret 61 | # key: username 62 | extraEnvVars: [] 63 | 64 | 65 | # If "installScope: cluster" then these labels will be applied to ClusterRole 66 | role: 67 | labels: {} 68 | 69 | metrics: 70 | service: 71 | # Set to true to automatically create a Kubernetes Service resource for the 72 | # Prometheus metrics server endpoint in controller 73 | create: false 74 | # Which Type to use for the Kubernetes Service? 75 | # See: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types 76 | type: "ClusterIP" 77 | 78 | resources: 79 | requests: 80 | memory: "64Mi" 81 | cpu: "50m" 82 | limits: 83 | memory: "128Mi" 84 | cpu: "100m" 85 | 86 | aws: 87 | # If specified, use the AWS region for AWS API calls 88 | region: "" 89 | endpoint_url: "" 90 | credentials: 91 | # If specified, Secret with shared credentials file to use. 92 | secretName: "" 93 | # Secret stringData key that contains the credentials 94 | secretKey: "credentials" 95 | # Profile used for AWS credentials 96 | profile: "default" 97 | 98 | # log level for the controller 99 | log: 100 | enable_development_logging: false 101 | level: info 102 | 103 | # Set to "namespace" to install the controller in a namespaced scope, will only 104 | # watch for object creation in the namespace. By default installScope is 105 | # cluster wide. 106 | installScope: cluster 107 | 108 | # Set the value of the "namespace" to be watched by the controller 109 | # This value is only used when the `installScope` is set to "namespace". If left empty, the default value is the release namespace for the chart. 110 | # You can set multiple namespaces by providing a comma separated list of namespaces. e.g "namespace1,namespace2" 111 | watchNamespace: "" 112 | 113 | # Set the value of labelsSelectors to be used by the controller to filter the resources to watch. 114 | # You can set multiple labelsSelectors by providing a comma separated list of a=b arguments. e.g "label1=value1,label2=value2" 115 | watchSelectors: "" 116 | 117 | resourceTags: 118 | # Configures the ACK service controller to always set key/value pairs tags on 119 | # resources that it manages. 120 | - services.k8s.aws/controller-version=%CONTROLLER_SERVICE%-%CONTROLLER_VERSION% 121 | - services.k8s.aws/namespace=%K8S_NAMESPACE% 122 | 123 | # Set to "retain" to keep all AWS resources intact even after the K8s resources 124 | # have been deleted. By default, the ACK controller will delete the AWS resource 125 | # before the K8s resource is removed. 126 | deletionPolicy: delete 127 | 128 | # controller reconciliation configurations 129 | reconcile: 130 | # The default duration, in seconds, to wait before resyncing desired state of custom resources. 131 | defaultResyncPeriod: 36000 # 10 Hours 132 | # An object representing the reconcile resync configuration for each specific resource. 133 | resourceResyncPeriods: {} 134 | 135 | # The default number of concurrent syncs that a reconciler can perform. 136 | defaultMaxConcurrentSyncs: 1 137 | # An object representing the reconcile max concurrent syncs configuration for each specific 138 | # resource. 139 | resourceMaxConcurrentSyncs: {} 140 | 141 | # Set the value of resources to specify which resource kinds to reconcile. 142 | # If empty, all resources will be reconciled. 143 | # If specified, only the listed resource kinds will be reconciled. 144 | resources: 145 | - PullThroughCacheRule 146 | - Repository 147 | 148 | serviceAccount: 149 | # Specifies whether a service account should be created 150 | create: true 151 | # The name of the service account to use. 152 | name: ack-ecr-controller 153 | annotations: {} 154 | # eks.amazonaws.com/role-arn: arn:aws:iam::AWS_ACCOUNT_ID:role/IAM_ROLE_NAME 155 | 156 | # Configuration of the leader election. Required for running multiple instances of the 157 | # controller within the same cluster. 158 | # See https://kubernetes.io/docs/concepts/architecture/leases/#leader-election 159 | leaderElection: 160 | # Enable Controller Leader Election. Set this to true to enable leader election 161 | # for this controller. 162 | enabled: false 163 | # Leader election can be scoped to a specific namespace. By default, the controller 164 | # will attempt to use the namespace of the service account mounted to the Controller 165 | # pod. 166 | namespace: "" 167 | 168 | # Configuration for feature gates. These are optional controller features that 169 | # can be individually enabled ("true") or disabled ("false") by adding key/value 170 | # pairs below. 171 | featureGates: 172 | # Enables the Service level granularity for CARM. See https://github.com/aws-controllers-k8s/community/issues/2031 173 | ServiceLevelCARM: false 174 | # Enables the Team level granularity for CARM. See https://github.com/aws-controllers-k8s/community/issues/2031 175 | TeamLevelCARM: false 176 | # Enable ReadOnlyResources feature/annotation. 177 | ReadOnlyResources: false 178 | # Enable ResourceAdoption feature/annotation. 179 | ResourceAdoption: false -------------------------------------------------------------------------------- /metadata.yaml: -------------------------------------------------------------------------------- 1 | service: 2 | full_name: "Amazon Elastic Container Registry" 3 | short_name: "ECR" 4 | link: "https://aws.amazon.com/ecr/" 5 | documentation: "https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html" 6 | api_versions: 7 | - api_version: v1alpha1 8 | status: available 9 | -------------------------------------------------------------------------------- /olm/olmconfig.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | annotations: 3 | capabilityLevel: Basic Install 4 | shortDescription: AWS ECR controller is a service controller for managing ECR resources 5 | in Kubernetes 6 | displayName: AWS Controllers for Kubernetes - Amazon ECR 7 | description: |- 8 | Manage Amazon Elastic Container Registry (ECR) resources in AWS from within your Kubernetes cluster. 9 | 10 | 11 | **About Amazon ECR** 12 | 13 | 14 | Amazon Elastic Container Registry (Amazon ECR) is an AWS managed container image registry service that is secure, scalable, and reliable. Amazon ECR supports private repositories with resource-based permissions using AWS IAM. This is so that specified users or Amazon EC2 instances can access your container repositories and images. You can use your preferred CLI to push, pull, and manage Docker images, Open Container Initiative (OCI) images, and OCI compatible artifacts. 15 | 16 | 17 | **About the AWS Controllers for Kubernetes** 18 | 19 | 20 | This controller is a component of the [AWS Controller for Kubernetes](https://github.com/aws/aws-controllers-k8s) 21 | project. 22 | 23 | 24 | **Pre-Installation Steps** 25 | 26 | 27 | Please follow the following link: [Red Hat OpenShift](https://aws-controllers-k8s.github.io/community/docs/user-docs/openshift/) 28 | samples: 29 | - kind: Repository 30 | spec: '{}' 31 | maintainers: 32 | - name: "ecr maintainer team" 33 | email: "ack-maintainers@amazon.com" 34 | links: 35 | - name: Amazon ECR Developer Resources 36 | url: https://aws.amazon.com/ecr/resources/ 37 | -------------------------------------------------------------------------------- /pkg/resource/pull_through_cache_rule/delta.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package pull_through_cache_rule 17 | 18 | import ( 19 | "bytes" 20 | "reflect" 21 | 22 | ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" 23 | acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" 24 | ) 25 | 26 | // Hack to avoid import errors during build... 27 | var ( 28 | _ = &bytes.Buffer{} 29 | _ = &reflect.Method{} 30 | _ = &acktags.Tags{} 31 | ) 32 | 33 | // newResourceDelta returns a new `ackcompare.Delta` used to compare two 34 | // resources 35 | func newResourceDelta( 36 | a *resource, 37 | b *resource, 38 | ) *ackcompare.Delta { 39 | delta := ackcompare.NewDelta() 40 | if (a == nil && b != nil) || 41 | (a != nil && b == nil) { 42 | delta.Add("", a, b) 43 | return delta 44 | } 45 | 46 | if ackcompare.HasNilDifference(a.ko.Spec.ECRRepositoryPrefix, b.ko.Spec.ECRRepositoryPrefix) { 47 | delta.Add("Spec.ECRRepositoryPrefix", a.ko.Spec.ECRRepositoryPrefix, b.ko.Spec.ECRRepositoryPrefix) 48 | } else if a.ko.Spec.ECRRepositoryPrefix != nil && b.ko.Spec.ECRRepositoryPrefix != nil { 49 | if *a.ko.Spec.ECRRepositoryPrefix != *b.ko.Spec.ECRRepositoryPrefix { 50 | delta.Add("Spec.ECRRepositoryPrefix", a.ko.Spec.ECRRepositoryPrefix, b.ko.Spec.ECRRepositoryPrefix) 51 | } 52 | } 53 | if ackcompare.HasNilDifference(a.ko.Spec.RegistryID, b.ko.Spec.RegistryID) { 54 | delta.Add("Spec.RegistryID", a.ko.Spec.RegistryID, b.ko.Spec.RegistryID) 55 | } else if a.ko.Spec.RegistryID != nil && b.ko.Spec.RegistryID != nil { 56 | if *a.ko.Spec.RegistryID != *b.ko.Spec.RegistryID { 57 | delta.Add("Spec.RegistryID", a.ko.Spec.RegistryID, b.ko.Spec.RegistryID) 58 | } 59 | } 60 | if ackcompare.HasNilDifference(a.ko.Spec.UpstreamRegistryURL, b.ko.Spec.UpstreamRegistryURL) { 61 | delta.Add("Spec.UpstreamRegistryURL", a.ko.Spec.UpstreamRegistryURL, b.ko.Spec.UpstreamRegistryURL) 62 | } else if a.ko.Spec.UpstreamRegistryURL != nil && b.ko.Spec.UpstreamRegistryURL != nil { 63 | if *a.ko.Spec.UpstreamRegistryURL != *b.ko.Spec.UpstreamRegistryURL { 64 | delta.Add("Spec.UpstreamRegistryURL", a.ko.Spec.UpstreamRegistryURL, b.ko.Spec.UpstreamRegistryURL) 65 | } 66 | } 67 | 68 | return delta 69 | } 70 | -------------------------------------------------------------------------------- /pkg/resource/pull_through_cache_rule/descriptor.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package pull_through_cache_rule 17 | 18 | import ( 19 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 20 | ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" 21 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 22 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | "k8s.io/apimachinery/pkg/runtime/schema" 24 | rtclient "sigs.k8s.io/controller-runtime/pkg/client" 25 | k8sctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 26 | 27 | svcapitypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1" 28 | ) 29 | 30 | const ( 31 | FinalizerString = "finalizers.ecr.services.k8s.aws/PullThroughCacheRule" 32 | ) 33 | 34 | var ( 35 | GroupVersionResource = svcapitypes.GroupVersion.WithResource("pullthroughcacherules") 36 | GroupKind = metav1.GroupKind{ 37 | Group: "ecr.services.k8s.aws", 38 | Kind: "PullThroughCacheRule", 39 | } 40 | ) 41 | 42 | // resourceDescriptor implements the 43 | // `aws-service-operator-k8s/pkg/types.AWSResourceDescriptor` interface 44 | type resourceDescriptor struct { 45 | } 46 | 47 | // GroupVersionKind returns a Kubernetes schema.GroupVersionKind struct that 48 | // describes the API Group, Version and Kind of CRs described by the descriptor 49 | func (d *resourceDescriptor) GroupVersionKind() schema.GroupVersionKind { 50 | return svcapitypes.GroupVersion.WithKind(GroupKind.Kind) 51 | } 52 | 53 | // EmptyRuntimeObject returns an empty object prototype that may be used in 54 | // apimachinery and k8s client operations 55 | func (d *resourceDescriptor) EmptyRuntimeObject() rtclient.Object { 56 | return &svcapitypes.PullThroughCacheRule{} 57 | } 58 | 59 | // ResourceFromRuntimeObject returns an AWSResource that has been initialized 60 | // with the supplied runtime.Object 61 | func (d *resourceDescriptor) ResourceFromRuntimeObject( 62 | obj rtclient.Object, 63 | ) acktypes.AWSResource { 64 | return &resource{ 65 | ko: obj.(*svcapitypes.PullThroughCacheRule), 66 | } 67 | } 68 | 69 | // Delta returns an `ackcompare.Delta` object containing the difference between 70 | // one `AWSResource` and another. 71 | func (d *resourceDescriptor) Delta(a, b acktypes.AWSResource) *ackcompare.Delta { 72 | return newResourceDelta(a.(*resource), b.(*resource)) 73 | } 74 | 75 | // IsManaged returns true if the supplied AWSResource is under the management 76 | // of an ACK service controller. What this means in practice is that the 77 | // underlying custom resource (CR) in the AWSResource has had a 78 | // resource-specific finalizer associated with it. 79 | func (d *resourceDescriptor) IsManaged( 80 | res acktypes.AWSResource, 81 | ) bool { 82 | obj := res.RuntimeObject() 83 | if obj == nil { 84 | // Should not happen. If it does, there is a bug in the code 85 | panic("nil RuntimeMetaObject in AWSResource") 86 | } 87 | // Remove use of custom code once 88 | // https://github.com/kubernetes-sigs/controller-runtime/issues/994 is 89 | // fixed. This should be able to be: 90 | // 91 | // return k8sctrlutil.ContainsFinalizer(obj, FinalizerString) 92 | return containsFinalizer(obj, FinalizerString) 93 | } 94 | 95 | // Remove once https://github.com/kubernetes-sigs/controller-runtime/issues/994 96 | // is fixed. 97 | func containsFinalizer(obj rtclient.Object, finalizer string) bool { 98 | f := obj.GetFinalizers() 99 | for _, e := range f { 100 | if e == finalizer { 101 | return true 102 | } 103 | } 104 | return false 105 | } 106 | 107 | // MarkManaged places the supplied resource under the management of ACK. What 108 | // this typically means is that the resource manager will decorate the 109 | // underlying custom resource (CR) with a finalizer that indicates ACK is 110 | // managing the resource and the underlying CR may not be deleted until ACK is 111 | // finished cleaning up any backend AWS service resources associated with the 112 | // CR. 113 | func (d *resourceDescriptor) MarkManaged( 114 | res acktypes.AWSResource, 115 | ) { 116 | obj := res.RuntimeObject() 117 | if obj == nil { 118 | // Should not happen. If it does, there is a bug in the code 119 | panic("nil RuntimeMetaObject in AWSResource") 120 | } 121 | k8sctrlutil.AddFinalizer(obj, FinalizerString) 122 | } 123 | 124 | // MarkUnmanaged removes the supplied resource from management by ACK. What 125 | // this typically means is that the resource manager will remove a finalizer 126 | // underlying custom resource (CR) that indicates ACK is managing the resource. 127 | // This will allow the Kubernetes API server to delete the underlying CR. 128 | func (d *resourceDescriptor) MarkUnmanaged( 129 | res acktypes.AWSResource, 130 | ) { 131 | obj := res.RuntimeObject() 132 | if obj == nil { 133 | // Should not happen. If it does, there is a bug in the code 134 | panic("nil RuntimeMetaObject in AWSResource") 135 | } 136 | k8sctrlutil.RemoveFinalizer(obj, FinalizerString) 137 | } 138 | 139 | // MarkAdopted places descriptors on the custom resource that indicate the 140 | // resource was not created from within ACK. 141 | func (d *resourceDescriptor) MarkAdopted( 142 | res acktypes.AWSResource, 143 | ) { 144 | obj := res.RuntimeObject() 145 | if obj == nil { 146 | // Should not happen. If it does, there is a bug in the code 147 | panic("nil RuntimeObject in AWSResource") 148 | } 149 | curr := obj.GetAnnotations() 150 | if curr == nil { 151 | curr = make(map[string]string) 152 | } 153 | curr[ackv1alpha1.AnnotationAdopted] = "true" 154 | obj.SetAnnotations(curr) 155 | } 156 | -------------------------------------------------------------------------------- /pkg/resource/pull_through_cache_rule/identifiers.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package pull_through_cache_rule 17 | 18 | import ( 19 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 20 | ) 21 | 22 | // resourceIdentifiers implements the 23 | // `aws-service-operator-k8s/pkg/types.AWSResourceIdentifiers` interface 24 | type resourceIdentifiers struct { 25 | meta *ackv1alpha1.ResourceMetadata 26 | } 27 | 28 | // ARN returns the AWS Resource Name for the backend AWS resource. If nil, 29 | // this means the resource has not yet been created in the backend AWS 30 | // service. 31 | func (ri *resourceIdentifiers) ARN() *ackv1alpha1.AWSResourceName { 32 | if ri.meta != nil { 33 | return ri.meta.ARN 34 | } 35 | return nil 36 | } 37 | 38 | // OwnerAccountID returns the AWS account identifier in which the 39 | // backend AWS resource resides, or nil if this information is not known 40 | // for the resource 41 | func (ri *resourceIdentifiers) OwnerAccountID() *ackv1alpha1.AWSAccountID { 42 | if ri.meta != nil { 43 | return ri.meta.OwnerAccountID 44 | } 45 | return nil 46 | } 47 | 48 | // Region returns the AWS region in which the resource exists, or 49 | // nil if this information is not known. 50 | func (ri *resourceIdentifiers) Region() *ackv1alpha1.AWSRegion { 51 | if ri.meta != nil { 52 | return ri.meta.Region 53 | } 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/resource/pull_through_cache_rule/manager_factory.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package pull_through_cache_rule 17 | 18 | import ( 19 | "fmt" 20 | "sync" 21 | 22 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 23 | ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" 24 | ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" 25 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 26 | "github.com/aws/aws-sdk-go-v2/aws" 27 | "github.com/go-logr/logr" 28 | 29 | svcresource "github.com/aws-controllers-k8s/ecr-controller/pkg/resource" 30 | ) 31 | 32 | // resourceManagerFactory produces resourceManager objects. It implements the 33 | // `types.AWSResourceManagerFactory` interface. 34 | type resourceManagerFactory struct { 35 | sync.RWMutex 36 | // rmCache contains resource managers for a particular AWS account ID 37 | rmCache map[string]*resourceManager 38 | } 39 | 40 | // ResourcePrototype returns an AWSResource that resource managers produced by 41 | // this factory will handle 42 | func (f *resourceManagerFactory) ResourceDescriptor() acktypes.AWSResourceDescriptor { 43 | return &resourceDescriptor{} 44 | } 45 | 46 | // ManagerFor returns a resource manager object that can manage resources for a 47 | // supplied AWS account 48 | func (f *resourceManagerFactory) ManagerFor( 49 | cfg ackcfg.Config, 50 | clientcfg aws.Config, 51 | log logr.Logger, 52 | metrics *ackmetrics.Metrics, 53 | rr acktypes.Reconciler, 54 | id ackv1alpha1.AWSAccountID, 55 | region ackv1alpha1.AWSRegion, 56 | roleARN ackv1alpha1.AWSResourceName, 57 | ) (acktypes.AWSResourceManager, error) { 58 | // We use the account ID, region, and role ARN to uniquely identify a 59 | // resource manager. This helps us to avoid creating multiple resource 60 | // managers for the same account/region/roleARN combination. 61 | rmId := fmt.Sprintf("%s/%s/%s", id, region, roleARN) 62 | f.RLock() 63 | rm, found := f.rmCache[rmId] 64 | f.RUnlock() 65 | 66 | if found { 67 | return rm, nil 68 | } 69 | 70 | f.Lock() 71 | defer f.Unlock() 72 | 73 | rm, err := newResourceManager(cfg, clientcfg, log, metrics, rr, id, region) 74 | if err != nil { 75 | return nil, err 76 | } 77 | f.rmCache[rmId] = rm 78 | return rm, nil 79 | } 80 | 81 | // IsAdoptable returns true if the resource is able to be adopted 82 | func (f *resourceManagerFactory) IsAdoptable() bool { 83 | return true 84 | } 85 | 86 | // RequeueOnSuccessSeconds returns true if the resource should be requeued after specified seconds 87 | // Default is false which means resource will not be requeued after success. 88 | func (f *resourceManagerFactory) RequeueOnSuccessSeconds() int { 89 | return 0 90 | } 91 | 92 | func newResourceManagerFactory() *resourceManagerFactory { 93 | return &resourceManagerFactory{ 94 | rmCache: map[string]*resourceManager{}, 95 | } 96 | } 97 | 98 | func init() { 99 | svcresource.RegisterManagerFactory(newResourceManagerFactory()) 100 | } 101 | -------------------------------------------------------------------------------- /pkg/resource/pull_through_cache_rule/references.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package pull_through_cache_rule 17 | 18 | import ( 19 | "context" 20 | 21 | "sigs.k8s.io/controller-runtime/pkg/client" 22 | 23 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 24 | 25 | svcapitypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1" 26 | ) 27 | 28 | // ClearResolvedReferences removes any reference values that were made 29 | // concrete in the spec. It returns a copy of the input AWSResource which 30 | // contains the original *Ref values, but none of their respective concrete 31 | // values. 32 | func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) acktypes.AWSResource { 33 | ko := rm.concreteResource(res).ko.DeepCopy() 34 | 35 | return &resource{ko} 36 | } 37 | 38 | // ResolveReferences finds if there are any Reference field(s) present 39 | // inside AWSResource passed in the parameter and attempts to resolve those 40 | // reference field(s) into their respective target field(s). It returns a 41 | // copy of the input AWSResource with resolved reference(s), a boolean which 42 | // is set to true if the resource contains any references (regardless of if 43 | // they are resolved successfully) and an error if the passed AWSResource's 44 | // reference field(s) could not be resolved. 45 | func (rm *resourceManager) ResolveReferences( 46 | ctx context.Context, 47 | apiReader client.Reader, 48 | res acktypes.AWSResource, 49 | ) (acktypes.AWSResource, bool, error) { 50 | return res, false, nil 51 | } 52 | 53 | // validateReferenceFields validates the reference field and corresponding 54 | // identifier field. 55 | func validateReferenceFields(ko *svcapitypes.PullThroughCacheRule) error { 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/resource/pull_through_cache_rule/resource.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package pull_through_cache_rule 17 | 18 | import ( 19 | "fmt" 20 | 21 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 22 | ackerrors "github.com/aws-controllers-k8s/runtime/pkg/errors" 23 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 24 | "github.com/aws/aws-sdk-go-v2/aws" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | rtclient "sigs.k8s.io/controller-runtime/pkg/client" 27 | 28 | svcapitypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1" 29 | ) 30 | 31 | // Hack to avoid import errors during build... 32 | var ( 33 | _ = &ackerrors.MissingNameIdentifier 34 | ) 35 | 36 | // resource implements the `aws-controller-k8s/runtime/pkg/types.AWSResource` 37 | // interface 38 | type resource struct { 39 | // The Kubernetes-native CR representing the resource 40 | ko *svcapitypes.PullThroughCacheRule 41 | } 42 | 43 | // Identifiers returns an AWSResourceIdentifiers object containing various 44 | // identifying information, including the AWS account ID that owns the 45 | // resource, the resource's AWS Resource Name (ARN) 46 | func (r *resource) Identifiers() acktypes.AWSResourceIdentifiers { 47 | return &resourceIdentifiers{r.ko.Status.ACKResourceMetadata} 48 | } 49 | 50 | // IsBeingDeleted returns true if the Kubernetes resource has a non-zero 51 | // deletion timestamp 52 | func (r *resource) IsBeingDeleted() bool { 53 | return !r.ko.DeletionTimestamp.IsZero() 54 | } 55 | 56 | // RuntimeObject returns the Kubernetes apimachinery/runtime representation of 57 | // the AWSResource 58 | func (r *resource) RuntimeObject() rtclient.Object { 59 | return r.ko 60 | } 61 | 62 | // MetaObject returns the Kubernetes apimachinery/apis/meta/v1.Object 63 | // representation of the AWSResource 64 | func (r *resource) MetaObject() metav1.Object { 65 | return r.ko.GetObjectMeta() 66 | } 67 | 68 | // Conditions returns the ACK Conditions collection for the AWSResource 69 | func (r *resource) Conditions() []*ackv1alpha1.Condition { 70 | return r.ko.Status.Conditions 71 | } 72 | 73 | // ReplaceConditions sets the Conditions status field for the resource 74 | func (r *resource) ReplaceConditions(conditions []*ackv1alpha1.Condition) { 75 | r.ko.Status.Conditions = conditions 76 | } 77 | 78 | // SetObjectMeta sets the ObjectMeta field for the resource 79 | func (r *resource) SetObjectMeta(meta metav1.ObjectMeta) { 80 | r.ko.ObjectMeta = meta 81 | } 82 | 83 | // SetStatus will set the Status field for the resource 84 | func (r *resource) SetStatus(desired acktypes.AWSResource) { 85 | r.ko.Status = desired.(*resource).ko.Status 86 | } 87 | 88 | // SetIdentifiers sets the Spec or Status field that is referenced as the unique 89 | // resource identifier 90 | func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error { 91 | if identifier.NameOrID == "" { 92 | return ackerrors.MissingNameIdentifier 93 | } 94 | r.ko.Spec.ECRRepositoryPrefix = &identifier.NameOrID 95 | 96 | f3, f3ok := identifier.AdditionalKeys["registryID"] 97 | if f3ok { 98 | r.ko.Spec.RegistryID = aws.String(f3) 99 | } 100 | 101 | return nil 102 | } 103 | 104 | // PopulateResourceFromAnnotation populates the fields passed from adoption annotation 105 | func (r *resource) PopulateResourceFromAnnotation(fields map[string]string) error { 106 | tmp, ok := fields["ecrRepositoryPrefix"] 107 | if !ok { 108 | return ackerrors.NewTerminalError(fmt.Errorf("required field missing: ecrRepositoryPrefix")) 109 | } 110 | r.ko.Spec.ECRRepositoryPrefix = &tmp 111 | 112 | f3, f3ok := fields["registryID"] 113 | if f3ok { 114 | r.ko.Spec.RegistryID = aws.String(f3) 115 | } 116 | 117 | return nil 118 | } 119 | 120 | // DeepCopy will return a copy of the resource 121 | func (r *resource) DeepCopy() acktypes.AWSResource { 122 | koCopy := r.ko.DeepCopy() 123 | return &resource{koCopy} 124 | } 125 | -------------------------------------------------------------------------------- /pkg/resource/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package resource 17 | 18 | import ( 19 | ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" 20 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 21 | ) 22 | 23 | // +kubebuilder:rbac:groups=services.k8s.aws,resources=adoptedresources,verbs=get;list;watch;create;update;patch;delete 24 | // +kubebuilder:rbac:groups=services.k8s.aws,resources=adoptedresources/status,verbs=get;update;patch 25 | // +kubebuilder:rbac:groups=services.k8s.aws,resources=fieldexports,verbs=get;list;watch;create;update;patch;delete 26 | // +kubebuilder:rbac:groups=services.k8s.aws,resources=fieldexports/status,verbs=get;update;patch 27 | // +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch 28 | // +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;patch 29 | // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;patch 30 | 31 | var ( 32 | reg = ackrt.NewRegistry() 33 | ) 34 | 35 | // GetManagerFactories returns a slice of resource manager factories that are 36 | // registered with this package 37 | func GetManagerFactories() []acktypes.AWSResourceManagerFactory { 38 | return reg.GetResourceManagerFactories() 39 | } 40 | 41 | // RegisterManagerFactory registers a resource manager factory with the 42 | // package's registry 43 | func RegisterManagerFactory(f acktypes.AWSResourceManagerFactory) { 44 | reg.RegisterResourceManagerFactory(f) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/resource/repository/delta.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package repository 17 | 18 | import ( 19 | "bytes" 20 | "reflect" 21 | 22 | ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" 23 | acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" 24 | ) 25 | 26 | // Hack to avoid import errors during build... 27 | var ( 28 | _ = &bytes.Buffer{} 29 | _ = &reflect.Method{} 30 | _ = &acktags.Tags{} 31 | ) 32 | 33 | // newResourceDelta returns a new `ackcompare.Delta` used to compare two 34 | // resources 35 | func newResourceDelta( 36 | a *resource, 37 | b *resource, 38 | ) *ackcompare.Delta { 39 | delta := ackcompare.NewDelta() 40 | if (a == nil && b != nil) || 41 | (a != nil && b == nil) { 42 | delta.Add("", a, b) 43 | return delta 44 | } 45 | customPreCompare(delta, a, b) 46 | 47 | if ackcompare.HasNilDifference(a.ko.Spec.EncryptionConfiguration, b.ko.Spec.EncryptionConfiguration) { 48 | delta.Add("Spec.EncryptionConfiguration", a.ko.Spec.EncryptionConfiguration, b.ko.Spec.EncryptionConfiguration) 49 | } else if a.ko.Spec.EncryptionConfiguration != nil && b.ko.Spec.EncryptionConfiguration != nil { 50 | if ackcompare.HasNilDifference(a.ko.Spec.EncryptionConfiguration.EncryptionType, b.ko.Spec.EncryptionConfiguration.EncryptionType) { 51 | delta.Add("Spec.EncryptionConfiguration.EncryptionType", a.ko.Spec.EncryptionConfiguration.EncryptionType, b.ko.Spec.EncryptionConfiguration.EncryptionType) 52 | } else if a.ko.Spec.EncryptionConfiguration.EncryptionType != nil && b.ko.Spec.EncryptionConfiguration.EncryptionType != nil { 53 | if *a.ko.Spec.EncryptionConfiguration.EncryptionType != *b.ko.Spec.EncryptionConfiguration.EncryptionType { 54 | delta.Add("Spec.EncryptionConfiguration.EncryptionType", a.ko.Spec.EncryptionConfiguration.EncryptionType, b.ko.Spec.EncryptionConfiguration.EncryptionType) 55 | } 56 | } 57 | if ackcompare.HasNilDifference(a.ko.Spec.EncryptionConfiguration.KMSKey, b.ko.Spec.EncryptionConfiguration.KMSKey) { 58 | delta.Add("Spec.EncryptionConfiguration.KMSKey", a.ko.Spec.EncryptionConfiguration.KMSKey, b.ko.Spec.EncryptionConfiguration.KMSKey) 59 | } else if a.ko.Spec.EncryptionConfiguration.KMSKey != nil && b.ko.Spec.EncryptionConfiguration.KMSKey != nil { 60 | if *a.ko.Spec.EncryptionConfiguration.KMSKey != *b.ko.Spec.EncryptionConfiguration.KMSKey { 61 | delta.Add("Spec.EncryptionConfiguration.KMSKey", a.ko.Spec.EncryptionConfiguration.KMSKey, b.ko.Spec.EncryptionConfiguration.KMSKey) 62 | } 63 | } 64 | } 65 | if ackcompare.HasNilDifference(a.ko.Spec.ImageScanningConfiguration, b.ko.Spec.ImageScanningConfiguration) { 66 | delta.Add("Spec.ImageScanningConfiguration", a.ko.Spec.ImageScanningConfiguration, b.ko.Spec.ImageScanningConfiguration) 67 | } else if a.ko.Spec.ImageScanningConfiguration != nil && b.ko.Spec.ImageScanningConfiguration != nil { 68 | if ackcompare.HasNilDifference(a.ko.Spec.ImageScanningConfiguration.ScanOnPush, b.ko.Spec.ImageScanningConfiguration.ScanOnPush) { 69 | delta.Add("Spec.ImageScanningConfiguration.ScanOnPush", a.ko.Spec.ImageScanningConfiguration.ScanOnPush, b.ko.Spec.ImageScanningConfiguration.ScanOnPush) 70 | } else if a.ko.Spec.ImageScanningConfiguration.ScanOnPush != nil && b.ko.Spec.ImageScanningConfiguration.ScanOnPush != nil { 71 | if *a.ko.Spec.ImageScanningConfiguration.ScanOnPush != *b.ko.Spec.ImageScanningConfiguration.ScanOnPush { 72 | delta.Add("Spec.ImageScanningConfiguration.ScanOnPush", a.ko.Spec.ImageScanningConfiguration.ScanOnPush, b.ko.Spec.ImageScanningConfiguration.ScanOnPush) 73 | } 74 | } 75 | } 76 | if ackcompare.HasNilDifference(a.ko.Spec.ImageTagMutability, b.ko.Spec.ImageTagMutability) { 77 | delta.Add("Spec.ImageTagMutability", a.ko.Spec.ImageTagMutability, b.ko.Spec.ImageTagMutability) 78 | } else if a.ko.Spec.ImageTagMutability != nil && b.ko.Spec.ImageTagMutability != nil { 79 | if *a.ko.Spec.ImageTagMutability != *b.ko.Spec.ImageTagMutability { 80 | delta.Add("Spec.ImageTagMutability", a.ko.Spec.ImageTagMutability, b.ko.Spec.ImageTagMutability) 81 | } 82 | } 83 | if ackcompare.HasNilDifference(a.ko.Spec.LifecyclePolicy, b.ko.Spec.LifecyclePolicy) { 84 | delta.Add("Spec.LifecyclePolicy", a.ko.Spec.LifecyclePolicy, b.ko.Spec.LifecyclePolicy) 85 | } else if a.ko.Spec.LifecyclePolicy != nil && b.ko.Spec.LifecyclePolicy != nil { 86 | if *a.ko.Spec.LifecyclePolicy != *b.ko.Spec.LifecyclePolicy { 87 | delta.Add("Spec.LifecyclePolicy", a.ko.Spec.LifecyclePolicy, b.ko.Spec.LifecyclePolicy) 88 | } 89 | } 90 | if ackcompare.HasNilDifference(a.ko.Spec.Name, b.ko.Spec.Name) { 91 | delta.Add("Spec.Name", a.ko.Spec.Name, b.ko.Spec.Name) 92 | } else if a.ko.Spec.Name != nil && b.ko.Spec.Name != nil { 93 | if *a.ko.Spec.Name != *b.ko.Spec.Name { 94 | delta.Add("Spec.Name", a.ko.Spec.Name, b.ko.Spec.Name) 95 | } 96 | } 97 | if ackcompare.HasNilDifference(a.ko.Spec.Policy, b.ko.Spec.Policy) { 98 | delta.Add("Spec.Policy", a.ko.Spec.Policy, b.ko.Spec.Policy) 99 | } else if a.ko.Spec.Policy != nil && b.ko.Spec.Policy != nil { 100 | if *a.ko.Spec.Policy != *b.ko.Spec.Policy { 101 | delta.Add("Spec.Policy", a.ko.Spec.Policy, b.ko.Spec.Policy) 102 | } 103 | } 104 | if ackcompare.HasNilDifference(a.ko.Spec.RegistryID, b.ko.Spec.RegistryID) { 105 | delta.Add("Spec.RegistryID", a.ko.Spec.RegistryID, b.ko.Spec.RegistryID) 106 | } else if a.ko.Spec.RegistryID != nil && b.ko.Spec.RegistryID != nil { 107 | if *a.ko.Spec.RegistryID != *b.ko.Spec.RegistryID { 108 | delta.Add("Spec.RegistryID", a.ko.Spec.RegistryID, b.ko.Spec.RegistryID) 109 | } 110 | } 111 | 112 | return delta 113 | } 114 | -------------------------------------------------------------------------------- /pkg/resource/repository/descriptor.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package repository 17 | 18 | import ( 19 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 20 | ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" 21 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 22 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | "k8s.io/apimachinery/pkg/runtime/schema" 24 | rtclient "sigs.k8s.io/controller-runtime/pkg/client" 25 | k8sctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 26 | 27 | svcapitypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1" 28 | ) 29 | 30 | const ( 31 | FinalizerString = "finalizers.ecr.services.k8s.aws/Repository" 32 | ) 33 | 34 | var ( 35 | GroupVersionResource = svcapitypes.GroupVersion.WithResource("repositories") 36 | GroupKind = metav1.GroupKind{ 37 | Group: "ecr.services.k8s.aws", 38 | Kind: "Repository", 39 | } 40 | ) 41 | 42 | // resourceDescriptor implements the 43 | // `aws-service-operator-k8s/pkg/types.AWSResourceDescriptor` interface 44 | type resourceDescriptor struct { 45 | } 46 | 47 | // GroupVersionKind returns a Kubernetes schema.GroupVersionKind struct that 48 | // describes the API Group, Version and Kind of CRs described by the descriptor 49 | func (d *resourceDescriptor) GroupVersionKind() schema.GroupVersionKind { 50 | return svcapitypes.GroupVersion.WithKind(GroupKind.Kind) 51 | } 52 | 53 | // EmptyRuntimeObject returns an empty object prototype that may be used in 54 | // apimachinery and k8s client operations 55 | func (d *resourceDescriptor) EmptyRuntimeObject() rtclient.Object { 56 | return &svcapitypes.Repository{} 57 | } 58 | 59 | // ResourceFromRuntimeObject returns an AWSResource that has been initialized 60 | // with the supplied runtime.Object 61 | func (d *resourceDescriptor) ResourceFromRuntimeObject( 62 | obj rtclient.Object, 63 | ) acktypes.AWSResource { 64 | return &resource{ 65 | ko: obj.(*svcapitypes.Repository), 66 | } 67 | } 68 | 69 | // Delta returns an `ackcompare.Delta` object containing the difference between 70 | // one `AWSResource` and another. 71 | func (d *resourceDescriptor) Delta(a, b acktypes.AWSResource) *ackcompare.Delta { 72 | return newResourceDelta(a.(*resource), b.(*resource)) 73 | } 74 | 75 | // IsManaged returns true if the supplied AWSResource is under the management 76 | // of an ACK service controller. What this means in practice is that the 77 | // underlying custom resource (CR) in the AWSResource has had a 78 | // resource-specific finalizer associated with it. 79 | func (d *resourceDescriptor) IsManaged( 80 | res acktypes.AWSResource, 81 | ) bool { 82 | obj := res.RuntimeObject() 83 | if obj == nil { 84 | // Should not happen. If it does, there is a bug in the code 85 | panic("nil RuntimeMetaObject in AWSResource") 86 | } 87 | // Remove use of custom code once 88 | // https://github.com/kubernetes-sigs/controller-runtime/issues/994 is 89 | // fixed. This should be able to be: 90 | // 91 | // return k8sctrlutil.ContainsFinalizer(obj, FinalizerString) 92 | return containsFinalizer(obj, FinalizerString) 93 | } 94 | 95 | // Remove once https://github.com/kubernetes-sigs/controller-runtime/issues/994 96 | // is fixed. 97 | func containsFinalizer(obj rtclient.Object, finalizer string) bool { 98 | f := obj.GetFinalizers() 99 | for _, e := range f { 100 | if e == finalizer { 101 | return true 102 | } 103 | } 104 | return false 105 | } 106 | 107 | // MarkManaged places the supplied resource under the management of ACK. What 108 | // this typically means is that the resource manager will decorate the 109 | // underlying custom resource (CR) with a finalizer that indicates ACK is 110 | // managing the resource and the underlying CR may not be deleted until ACK is 111 | // finished cleaning up any backend AWS service resources associated with the 112 | // CR. 113 | func (d *resourceDescriptor) MarkManaged( 114 | res acktypes.AWSResource, 115 | ) { 116 | obj := res.RuntimeObject() 117 | if obj == nil { 118 | // Should not happen. If it does, there is a bug in the code 119 | panic("nil RuntimeMetaObject in AWSResource") 120 | } 121 | k8sctrlutil.AddFinalizer(obj, FinalizerString) 122 | } 123 | 124 | // MarkUnmanaged removes the supplied resource from management by ACK. What 125 | // this typically means is that the resource manager will remove a finalizer 126 | // underlying custom resource (CR) that indicates ACK is managing the resource. 127 | // This will allow the Kubernetes API server to delete the underlying CR. 128 | func (d *resourceDescriptor) MarkUnmanaged( 129 | res acktypes.AWSResource, 130 | ) { 131 | obj := res.RuntimeObject() 132 | if obj == nil { 133 | // Should not happen. If it does, there is a bug in the code 134 | panic("nil RuntimeMetaObject in AWSResource") 135 | } 136 | k8sctrlutil.RemoveFinalizer(obj, FinalizerString) 137 | } 138 | 139 | // MarkAdopted places descriptors on the custom resource that indicate the 140 | // resource was not created from within ACK. 141 | func (d *resourceDescriptor) MarkAdopted( 142 | res acktypes.AWSResource, 143 | ) { 144 | obj := res.RuntimeObject() 145 | if obj == nil { 146 | // Should not happen. If it does, there is a bug in the code 147 | panic("nil RuntimeObject in AWSResource") 148 | } 149 | curr := obj.GetAnnotations() 150 | if curr == nil { 151 | curr = make(map[string]string) 152 | } 153 | curr[ackv1alpha1.AnnotationAdopted] = "true" 154 | obj.SetAnnotations(curr) 155 | } 156 | -------------------------------------------------------------------------------- /pkg/resource/repository/hook.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "strconv" 7 | 8 | ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" 9 | ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" 10 | ackutil "github.com/aws-controllers-k8s/runtime/pkg/util" 11 | svcsdk "github.com/aws/aws-sdk-go-v2/service/ecr" 12 | svcsdktypes "github.com/aws/aws-sdk-go-v2/service/ecr/types" 13 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | 15 | svcapitypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1" 16 | ) 17 | 18 | const ( 19 | // AnnotationPrefix is the prefix for all annotations specifically for 20 | // the ECR service. 21 | AnnotationPrefix = "ecr.services.k8s.aws/" 22 | // AnnotationDeleteForce is an annotation whose value indicates whether 23 | // the repository should be removed if it contains images. 24 | AnnotationDeleteForce = AnnotationPrefix + "force-delete" 25 | 26 | DefaultDeleteForce = false 27 | ) 28 | 29 | // GetDeleteForce returns whether the repository should be deleted if it 30 | // contains images as determined by the annotation on the object, or the 31 | // default value otherwise. 32 | func GetDeleteForce( 33 | m *metav1.ObjectMeta, 34 | ) bool { 35 | resAnnotations := m.GetAnnotations() 36 | deleteForce, ok := resAnnotations[AnnotationDeleteForce] 37 | if !ok { 38 | return DefaultDeleteForce 39 | } 40 | 41 | deleteForceBool, err := strconv.ParseBool(deleteForce) 42 | if err != nil { 43 | return DefaultDeleteForce 44 | } 45 | 46 | return deleteForceBool 47 | } 48 | 49 | // setResourceAdditionalFields will describe the fields that are not return by 50 | // DescribeRepository calls 51 | func (rm *resourceManager) setResourceAdditionalFields( 52 | ctx context.Context, 53 | ko *svcapitypes.Repository, 54 | ) (err error) { 55 | rlog := ackrtlog.FromContext(ctx) 56 | exit := rlog.Trace("rm.setResourceAdditionalFields") 57 | defer exit(err) 58 | 59 | // Set repository policy 60 | ko.Spec.Policy, err = rm.getRepositoryPolicy(ctx, *ko.Spec.Name, *ko.Spec.RegistryID) 61 | if err != nil { 62 | return err 63 | } 64 | // Set repository lifecycle policy 65 | ko.Spec.LifecyclePolicy, err = rm.getRepositoryLifecyclePolicy(ctx, *ko.Spec.Name, *ko.Spec.RegistryID) 66 | if err != nil { 67 | return err 68 | } 69 | // Set repository tags 70 | ko.Spec.Tags, err = rm.getRepositoryTags(ctx, string(*ko.Status.ACKResourceMetadata.ARN)) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | return nil 76 | } 77 | 78 | // getRepositoryPolicy retrieves a repository permissions policy. 79 | func (rm *resourceManager) getRepositoryPolicy( 80 | ctx context.Context, 81 | repositoryName, 82 | registryID string, 83 | ) (*string, error) { 84 | rlog := ackrtlog.FromContext(ctx) 85 | exit := rlog.Trace("rm.getRepositoryPolicy") 86 | var err error 87 | defer exit(err) 88 | 89 | var getRepositoryPolicyResponse *svcsdk.GetRepositoryPolicyOutput 90 | getRepositoryPolicyResponse, err = rm.sdkapi.GetRepositoryPolicy( 91 | ctx, 92 | &svcsdk.GetRepositoryPolicyInput{ 93 | RepositoryName: &repositoryName, 94 | RegistryId: ®istryID, 95 | }, 96 | ) 97 | rm.metrics.RecordAPICall("GET", "GetRepositoryPolicy", err) 98 | if err != nil { 99 | var notFound *svcsdktypes.RepositoryPolicyNotFoundException 100 | if !errors.As(err, ¬Found) { 101 | return nil, err 102 | } 103 | // do not return an error if the repository policy is not found. Simply return an empty policy. 104 | return nil, nil 105 | } 106 | return getRepositoryPolicyResponse.PolicyText, nil 107 | } 108 | 109 | // getRepositoryLifecyclePolicy retrieves a repository lifecycle policy. 110 | func (rm *resourceManager) getRepositoryLifecyclePolicy( 111 | ctx context.Context, 112 | repositoryName, 113 | registryID string, 114 | ) (*string, error) { 115 | rlog := ackrtlog.FromContext(ctx) 116 | exit := rlog.Trace("rm.getRepositoryLifecyclePolicy") 117 | var err error 118 | defer exit(err) 119 | 120 | var getLifecyclePolicyResponse *svcsdk.GetLifecyclePolicyOutput 121 | getLifecyclePolicyResponse, err = rm.sdkapi.GetLifecyclePolicy( 122 | ctx, 123 | &svcsdk.GetLifecyclePolicyInput{ 124 | RepositoryName: &repositoryName, 125 | RegistryId: ®istryID, 126 | }, 127 | ) 128 | rm.metrics.RecordAPICall("GET", "GetLifecyclePolicy", err) 129 | if err != nil { 130 | var notFound *svcsdktypes.LifecyclePolicyNotFoundException 131 | if !errors.As(err, ¬Found) { 132 | return nil, err 133 | } 134 | // do not return an error if the lifecycle policy is not found. Simply return an empty lifecycle policy. 135 | return nil, nil 136 | } 137 | return getLifecyclePolicyResponse.LifecyclePolicyText, nil 138 | } 139 | 140 | // getRepositoryTags retrieves a resource list of tags. 141 | func (rm *resourceManager) getRepositoryTags(ctx context.Context, resourceARN string) ([]*svcapitypes.Tag, error) { 142 | listTagsForResourceResponse, err := rm.sdkapi.ListTagsForResource( 143 | ctx, 144 | &svcsdk.ListTagsForResourceInput{ 145 | ResourceArn: &resourceARN, 146 | }, 147 | ) 148 | rm.metrics.RecordAPICall("GET", "ListTagsForResource", err) 149 | if err != nil { 150 | return nil, err 151 | } 152 | tags := make([]*svcapitypes.Tag, 0, len(listTagsForResourceResponse.Tags)) 153 | for _, tag := range listTagsForResourceResponse.Tags { 154 | tags = append(tags, &svcapitypes.Tag{ 155 | Key: tag.Key, 156 | Value: tag.Value, 157 | }) 158 | } 159 | return tags, nil 160 | } 161 | 162 | func customPreCompare( 163 | delta *ackcompare.Delta, 164 | a *resource, 165 | b *resource, 166 | ) { 167 | if len(a.ko.Spec.Tags) != len(b.ko.Spec.Tags) { 168 | delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags) 169 | } else if len(a.ko.Spec.Tags) > 0 { 170 | if !equalTags(a.ko.Spec.Tags, b.ko.Spec.Tags) { 171 | delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags) 172 | } 173 | } 174 | } 175 | 176 | // equalTags returns true if two Tag arrays are equal regardless of the order 177 | // of their elements. 178 | func equalTags( 179 | a []*svcapitypes.Tag, 180 | b []*svcapitypes.Tag, 181 | ) bool { 182 | added, updated, removed := computeTagsDelta(a, b) 183 | return len(added) == 0 && len(updated) == 0 && len(removed) == 0 184 | } 185 | 186 | // computeTagsDelta compares two Tag arrays and return three different list 187 | // containing the added, updated and removed tags. 188 | // The removed tags only contains the Key of tags 189 | func computeTagsDelta( 190 | a []*svcapitypes.Tag, 191 | b []*svcapitypes.Tag, 192 | ) (added, updated []*svcapitypes.Tag, removed []*string) { 193 | var visitedIndexes []string 194 | mainLoop: 195 | for _, aElement := range a { 196 | visitedIndexes = append(visitedIndexes, *aElement.Key) 197 | for _, bElement := range b { 198 | if equalStrings(aElement.Key, bElement.Key) { 199 | if !equalStrings(aElement.Value, bElement.Value) { 200 | updated = append(updated, bElement) 201 | } 202 | continue mainLoop 203 | } 204 | } 205 | removed = append(removed, aElement.Key) 206 | } 207 | for _, bElement := range b { 208 | if !ackutil.InStrings(*bElement.Key, visitedIndexes) { 209 | added = append(added, bElement) 210 | } 211 | } 212 | return added, updated, removed 213 | } 214 | 215 | // svcTagsFromResourceTags transforms a *svcapitypes.Tag array to a *svcsdk.Tag array. 216 | func sdkTagsFromResourceTags(rTags []*svcapitypes.Tag) []svcsdktypes.Tag { 217 | tags := make([]svcsdktypes.Tag, len(rTags)) 218 | for i := range rTags { 219 | tags[i] = svcsdktypes.Tag{ 220 | Key: rTags[i].Key, 221 | Value: rTags[i].Value, 222 | } 223 | } 224 | return tags 225 | } 226 | 227 | func equalStrings(a, b *string) bool { 228 | if a == nil { 229 | return b == nil || *b == "" 230 | } 231 | return (*a == "" && b == nil) || *a == *b 232 | } 233 | -------------------------------------------------------------------------------- /pkg/resource/repository/hook_test.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | package repository_test 15 | 16 | import ( 17 | "testing" 18 | 19 | repo "github.com/aws-controllers-k8s/ecr-controller/pkg/resource/repository" 20 | "github.com/stretchr/testify/assert" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | ) 23 | 24 | func Test_GetDeleteForce(t *testing.T) { 25 | assert := assert.New(t) 26 | 27 | noAnnotation := metav1.ObjectMeta{ 28 | Annotations: map[string]string{}, 29 | } 30 | badAnnotation := metav1.ObjectMeta{ 31 | Annotations: map[string]string{ 32 | repo.AnnotationDeleteForce: "not-a-bool", 33 | }, 34 | } 35 | validAnnotation := metav1.ObjectMeta{ 36 | Annotations: map[string]string{ 37 | repo.AnnotationDeleteForce: "true", 38 | }, 39 | } 40 | 41 | assert.Equal(repo.GetDeleteForce(&noAnnotation), repo.DefaultDeleteForce) 42 | assert.Equal(repo.GetDeleteForce(&badAnnotation), repo.DefaultDeleteForce) 43 | assert.Equal(repo.GetDeleteForce(&validAnnotation), true) 44 | } 45 | -------------------------------------------------------------------------------- /pkg/resource/repository/identifiers.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package repository 17 | 18 | import ( 19 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 20 | ) 21 | 22 | // resourceIdentifiers implements the 23 | // `aws-service-operator-k8s/pkg/types.AWSResourceIdentifiers` interface 24 | type resourceIdentifiers struct { 25 | meta *ackv1alpha1.ResourceMetadata 26 | } 27 | 28 | // ARN returns the AWS Resource Name for the backend AWS resource. If nil, 29 | // this means the resource has not yet been created in the backend AWS 30 | // service. 31 | func (ri *resourceIdentifiers) ARN() *ackv1alpha1.AWSResourceName { 32 | if ri.meta != nil { 33 | return ri.meta.ARN 34 | } 35 | return nil 36 | } 37 | 38 | // OwnerAccountID returns the AWS account identifier in which the 39 | // backend AWS resource resides, or nil if this information is not known 40 | // for the resource 41 | func (ri *resourceIdentifiers) OwnerAccountID() *ackv1alpha1.AWSAccountID { 42 | if ri.meta != nil { 43 | return ri.meta.OwnerAccountID 44 | } 45 | return nil 46 | } 47 | 48 | // Region returns the AWS region in which the resource exists, or 49 | // nil if this information is not known. 50 | func (ri *resourceIdentifiers) Region() *ackv1alpha1.AWSRegion { 51 | if ri.meta != nil { 52 | return ri.meta.Region 53 | } 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/resource/repository/manager_factory.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package repository 17 | 18 | import ( 19 | "fmt" 20 | "sync" 21 | 22 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 23 | ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" 24 | ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" 25 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 26 | "github.com/aws/aws-sdk-go-v2/aws" 27 | "github.com/go-logr/logr" 28 | 29 | svcresource "github.com/aws-controllers-k8s/ecr-controller/pkg/resource" 30 | ) 31 | 32 | // resourceManagerFactory produces resourceManager objects. It implements the 33 | // `types.AWSResourceManagerFactory` interface. 34 | type resourceManagerFactory struct { 35 | sync.RWMutex 36 | // rmCache contains resource managers for a particular AWS account ID 37 | rmCache map[string]*resourceManager 38 | } 39 | 40 | // ResourcePrototype returns an AWSResource that resource managers produced by 41 | // this factory will handle 42 | func (f *resourceManagerFactory) ResourceDescriptor() acktypes.AWSResourceDescriptor { 43 | return &resourceDescriptor{} 44 | } 45 | 46 | // ManagerFor returns a resource manager object that can manage resources for a 47 | // supplied AWS account 48 | func (f *resourceManagerFactory) ManagerFor( 49 | cfg ackcfg.Config, 50 | clientcfg aws.Config, 51 | log logr.Logger, 52 | metrics *ackmetrics.Metrics, 53 | rr acktypes.Reconciler, 54 | id ackv1alpha1.AWSAccountID, 55 | region ackv1alpha1.AWSRegion, 56 | roleARN ackv1alpha1.AWSResourceName, 57 | ) (acktypes.AWSResourceManager, error) { 58 | // We use the account ID, region, and role ARN to uniquely identify a 59 | // resource manager. This helps us to avoid creating multiple resource 60 | // managers for the same account/region/roleARN combination. 61 | rmId := fmt.Sprintf("%s/%s/%s", id, region, roleARN) 62 | f.RLock() 63 | rm, found := f.rmCache[rmId] 64 | f.RUnlock() 65 | 66 | if found { 67 | return rm, nil 68 | } 69 | 70 | f.Lock() 71 | defer f.Unlock() 72 | 73 | rm, err := newResourceManager(cfg, clientcfg, log, metrics, rr, id, region) 74 | if err != nil { 75 | return nil, err 76 | } 77 | f.rmCache[rmId] = rm 78 | return rm, nil 79 | } 80 | 81 | // IsAdoptable returns true if the resource is able to be adopted 82 | func (f *resourceManagerFactory) IsAdoptable() bool { 83 | return true 84 | } 85 | 86 | // RequeueOnSuccessSeconds returns true if the resource should be requeued after specified seconds 87 | // Default is false which means resource will not be requeued after success. 88 | func (f *resourceManagerFactory) RequeueOnSuccessSeconds() int { 89 | return 0 90 | } 91 | 92 | func newResourceManagerFactory() *resourceManagerFactory { 93 | return &resourceManagerFactory{ 94 | rmCache: map[string]*resourceManager{}, 95 | } 96 | } 97 | 98 | func init() { 99 | svcresource.RegisterManagerFactory(newResourceManagerFactory()) 100 | } 101 | -------------------------------------------------------------------------------- /pkg/resource/repository/references.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package repository 17 | 18 | import ( 19 | "context" 20 | 21 | "sigs.k8s.io/controller-runtime/pkg/client" 22 | 23 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 24 | 25 | svcapitypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1" 26 | ) 27 | 28 | // ClearResolvedReferences removes any reference values that were made 29 | // concrete in the spec. It returns a copy of the input AWSResource which 30 | // contains the original *Ref values, but none of their respective concrete 31 | // values. 32 | func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) acktypes.AWSResource { 33 | ko := rm.concreteResource(res).ko.DeepCopy() 34 | 35 | return &resource{ko} 36 | } 37 | 38 | // ResolveReferences finds if there are any Reference field(s) present 39 | // inside AWSResource passed in the parameter and attempts to resolve those 40 | // reference field(s) into their respective target field(s). It returns a 41 | // copy of the input AWSResource with resolved reference(s), a boolean which 42 | // is set to true if the resource contains any references (regardless of if 43 | // they are resolved successfully) and an error if the passed AWSResource's 44 | // reference field(s) could not be resolved. 45 | func (rm *resourceManager) ResolveReferences( 46 | ctx context.Context, 47 | apiReader client.Reader, 48 | res acktypes.AWSResource, 49 | ) (acktypes.AWSResource, bool, error) { 50 | return res, false, nil 51 | } 52 | 53 | // validateReferenceFields validates the reference field and corresponding 54 | // identifier field. 55 | func validateReferenceFields(ko *svcapitypes.Repository) error { 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/resource/repository/resource.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package repository 17 | 18 | import ( 19 | "fmt" 20 | 21 | ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 22 | ackerrors "github.com/aws-controllers-k8s/runtime/pkg/errors" 23 | acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 24 | "github.com/aws/aws-sdk-go-v2/aws" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | rtclient "sigs.k8s.io/controller-runtime/pkg/client" 27 | 28 | svcapitypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1" 29 | ) 30 | 31 | // Hack to avoid import errors during build... 32 | var ( 33 | _ = &ackerrors.MissingNameIdentifier 34 | ) 35 | 36 | // resource implements the `aws-controller-k8s/runtime/pkg/types.AWSResource` 37 | // interface 38 | type resource struct { 39 | // The Kubernetes-native CR representing the resource 40 | ko *svcapitypes.Repository 41 | } 42 | 43 | // Identifiers returns an AWSResourceIdentifiers object containing various 44 | // identifying information, including the AWS account ID that owns the 45 | // resource, the resource's AWS Resource Name (ARN) 46 | func (r *resource) Identifiers() acktypes.AWSResourceIdentifiers { 47 | return &resourceIdentifiers{r.ko.Status.ACKResourceMetadata} 48 | } 49 | 50 | // IsBeingDeleted returns true if the Kubernetes resource has a non-zero 51 | // deletion timestamp 52 | func (r *resource) IsBeingDeleted() bool { 53 | return !r.ko.DeletionTimestamp.IsZero() 54 | } 55 | 56 | // RuntimeObject returns the Kubernetes apimachinery/runtime representation of 57 | // the AWSResource 58 | func (r *resource) RuntimeObject() rtclient.Object { 59 | return r.ko 60 | } 61 | 62 | // MetaObject returns the Kubernetes apimachinery/apis/meta/v1.Object 63 | // representation of the AWSResource 64 | func (r *resource) MetaObject() metav1.Object { 65 | return r.ko.GetObjectMeta() 66 | } 67 | 68 | // Conditions returns the ACK Conditions collection for the AWSResource 69 | func (r *resource) Conditions() []*ackv1alpha1.Condition { 70 | return r.ko.Status.Conditions 71 | } 72 | 73 | // ReplaceConditions sets the Conditions status field for the resource 74 | func (r *resource) ReplaceConditions(conditions []*ackv1alpha1.Condition) { 75 | r.ko.Status.Conditions = conditions 76 | } 77 | 78 | // SetObjectMeta sets the ObjectMeta field for the resource 79 | func (r *resource) SetObjectMeta(meta metav1.ObjectMeta) { 80 | r.ko.ObjectMeta = meta 81 | } 82 | 83 | // SetStatus will set the Status field for the resource 84 | func (r *resource) SetStatus(desired acktypes.AWSResource) { 85 | r.ko.Status = desired.(*resource).ko.Status 86 | } 87 | 88 | // SetIdentifiers sets the Spec or Status field that is referenced as the unique 89 | // resource identifier 90 | func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error { 91 | if identifier.NameOrID == "" { 92 | return ackerrors.MissingNameIdentifier 93 | } 94 | r.ko.Spec.Name = &identifier.NameOrID 95 | 96 | f2, f2ok := identifier.AdditionalKeys["registryID"] 97 | if f2ok { 98 | r.ko.Spec.RegistryID = aws.String(f2) 99 | } 100 | 101 | return nil 102 | } 103 | 104 | // PopulateResourceFromAnnotation populates the fields passed from adoption annotation 105 | func (r *resource) PopulateResourceFromAnnotation(fields map[string]string) error { 106 | tmp, ok := fields["name"] 107 | if !ok { 108 | return ackerrors.NewTerminalError(fmt.Errorf("required field missing: name")) 109 | } 110 | r.ko.Spec.Name = &tmp 111 | 112 | f2, f2ok := fields["registryID"] 113 | if f2ok { 114 | r.ko.Spec.RegistryID = aws.String(f2) 115 | } 116 | 117 | return nil 118 | } 119 | 120 | // DeepCopy will return a copy of the resource 121 | func (r *resource) DeepCopy() acktypes.AWSResource { 122 | koCopy := r.ko.DeepCopy() 123 | return &resource{koCopy} 124 | } 125 | -------------------------------------------------------------------------------- /pkg/resource/repository/tags.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package repository 17 | 18 | import ( 19 | "slices" 20 | "strings" 21 | 22 | acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" 23 | 24 | svcapitypes "github.com/aws-controllers-k8s/ecr-controller/apis/v1alpha1" 25 | ) 26 | 27 | var ( 28 | _ = svcapitypes.Repository{} 29 | _ = acktags.NewTags() 30 | ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"} 31 | ) 32 | 33 | // convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape. 34 | // This method helps in creating the hub(acktags.Tags) for merging 35 | // default controller tags with existing resource tags. It also returns a slice 36 | // of keys maintaining the original key Order when the tags are a list 37 | func convertToOrderedACKTags(tags []*svcapitypes.Tag) (acktags.Tags, []string) { 38 | result := acktags.NewTags() 39 | keyOrder := []string{} 40 | 41 | if len(tags) == 0 { 42 | return result, keyOrder 43 | } 44 | for _, t := range tags { 45 | if t.Key != nil { 46 | keyOrder = append(keyOrder, *t.Key) 47 | if t.Value != nil { 48 | result[*t.Key] = *t.Value 49 | } else { 50 | result[*t.Key] = "" 51 | } 52 | } 53 | } 54 | 55 | return result, keyOrder 56 | } 57 | 58 | // fromACKTags converts the tags parameter into []*svcapitypes.Tag shape. 59 | // This method helps in setting the tags back inside AWSResource after merging 60 | // default controller tags with existing resource tags. When a list, 61 | // it maintains the order from original 62 | func fromACKTags(tags acktags.Tags, keyOrder []string) []*svcapitypes.Tag { 63 | result := []*svcapitypes.Tag{} 64 | 65 | for _, k := range keyOrder { 66 | v, ok := tags[k] 67 | if ok { 68 | tag := svcapitypes.Tag{Key: &k, Value: &v} 69 | result = append(result, &tag) 70 | delete(tags, k) 71 | } 72 | } 73 | for k, v := range tags { 74 | tag := svcapitypes.Tag{Key: &k, Value: &v} 75 | result = append(result, &tag) 76 | } 77 | 78 | return result 79 | } 80 | 81 | // ignoreSystemTags ignores tags that have keys that start with "aws:" 82 | // and ACKSystemTags, to avoid patching them to the resourceSpec. 83 | // Eg. resources created with cloudformation have tags that cannot be 84 | // removed by an ACK controller 85 | func ignoreSystemTags(tags acktags.Tags) { 86 | for k := range tags { 87 | if strings.HasPrefix(k, "aws:") || 88 | slices.Contains(ACKSystemTags, k) { 89 | delete(tags, k) 90 | } 91 | } 92 | } 93 | 94 | // syncAWSTags ensures AWS-managed tags (prefixed with "aws:") from the latest resource state 95 | // are preserved in the desired state. This prevents the controller from attempting to 96 | // modify AWS-managed tags, which would result in an error. 97 | // 98 | // AWS-managed tags are automatically added by AWS services (e.g., CloudFormation, Service Catalog) 99 | // and cannot be modified or deleted through normal tag operations. Common examples include: 100 | // - aws:cloudformation:stack-name 101 | // - aws:servicecatalog:productArn 102 | // 103 | // Parameters: 104 | // - a: The target Tags map to be updated (typically desired state) 105 | // - b: The source Tags map containing AWS-managed tags (typically latest state) 106 | // 107 | // Example: 108 | // 109 | // latest := Tags{"aws:cloudformation:stack-name": "my-stack", "environment": "prod"} 110 | // desired := Tags{"environment": "dev"} 111 | // SyncAWSTags(desired, latest) 112 | // desired now contains {"aws:cloudformation:stack-name": "my-stack", "environment": "dev"} 113 | func syncAWSTags(a acktags.Tags, b acktags.Tags) { 114 | for k := range b { 115 | if strings.HasPrefix(k, "aws:") { 116 | a[k] = b[k] 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | // not use this file except in compliance with the License. A copy of the 5 | // License is located at 6 | // 7 | // http://aws.amazon.com/apache2.0/ 8 | // 9 | // or in the "license" file accompanying this file. This file is distributed 10 | // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | // express or implied. See the License for the specific language governing 12 | // permissions and limitations under the License. 13 | 14 | // Code generated by ack-generate. DO NOT EDIT. 15 | 16 | package version 17 | 18 | var ( 19 | GitVersion string 20 | GitCommit string 21 | BuildDate string 22 | ) 23 | -------------------------------------------------------------------------------- /templates/hooks/pull_through_cache_rule/sdk_read_many_post_build_request.go.tpl: -------------------------------------------------------------------------------- 1 | if r.ko.Spec.ECRRepositoryPrefix != nil { 2 | input.EcrRepositoryPrefixes = []string{*r.ko.Spec.ECRRepositoryPrefix} 3 | } -------------------------------------------------------------------------------- /templates/hooks/repository/sdk_create_post_set_output.go.tpl: -------------------------------------------------------------------------------- 1 | // Set the repository policy 2 | if ko.Spec.Policy != nil && *ko.Spec.Policy != "" { 3 | if _, err := rm.updateRepositoryPolicy(ctx, desired); err != nil{ 4 | return nil, err 5 | } 6 | } 7 | // Set the lifecycle policy 8 | if ko.Spec.LifecyclePolicy != nil && *ko.Spec.LifecyclePolicy != "" { 9 | if _, err := rm.updateLifecyclePolicy(ctx, desired); err != nil{ 10 | return nil, err 11 | } 12 | } -------------------------------------------------------------------------------- /templates/hooks/repository/sdk_delete_post_build_request.go.tpl: -------------------------------------------------------------------------------- 1 | input.Force = GetDeleteForce(&r.ko.ObjectMeta) -------------------------------------------------------------------------------- /templates/hooks/repository/sdk_read_many_post_set_output.go.tpl: -------------------------------------------------------------------------------- 1 | if err := rm.setResourceAdditionalFields(ctx, ko); err != nil { 2 | return nil, err 3 | } -------------------------------------------------------------------------------- /test/e2e/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | **/bootstrap.yaml -------------------------------------------------------------------------------- /test/e2e/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | 14 | import pytest 15 | from typing import Dict, Any 16 | from pathlib import Path 17 | 18 | from acktest.resources import load_resource_file 19 | 20 | SERVICE_NAME = "ecr" 21 | CRD_GROUP = "ecr.services.k8s.aws" 22 | CRD_VERSION = "v1alpha1" 23 | 24 | # PyTest marker for the current service 25 | service_marker = pytest.mark.service(arg=SERVICE_NAME) 26 | 27 | bootstrap_directory = Path(__file__).parent 28 | resource_directory = Path(__file__).parent / "resources" 29 | def load_ecr_resource(resource_name: str, additional_replacements: Dict[str, Any] = {}): 30 | """ Overrides the default `load_resource_file` to access the specific resources 31 | directory for the current service. 32 | """ 33 | return load_resource_file(resource_directory, resource_name, additional_replacements=additional_replacements) -------------------------------------------------------------------------------- /test/e2e/bootstrap_resources.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | 14 | """Declares the structure of the bootstrapped resources and provides a loader 15 | for them. 16 | """ 17 | 18 | from dataclasses import dataclass 19 | from acktest.bootstrapping import Resources 20 | from e2e import bootstrap_directory 21 | 22 | @dataclass 23 | class BootstrapResources(Resources): 24 | pass 25 | 26 | _bootstrap_resources = None 27 | 28 | def get_bootstrap_resources(bootstrap_file_name: str = "bootstrap.pkl") -> BootstrapResources: 29 | global _bootstrap_resources 30 | if _bootstrap_resources is None: 31 | _bootstrap_resources = BootstrapResources.deserialize(bootstrap_directory, bootstrap_file_name=bootstrap_file_name) 32 | return _bootstrap_resources 33 | -------------------------------------------------------------------------------- /test/e2e/conftest.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | 14 | import boto3 15 | import pytest 16 | 17 | from acktest import k8s 18 | 19 | 20 | def pytest_addoption(parser): 21 | parser.addoption("--runslow", action="store_true", default=False, help="run slow tests") 22 | 23 | 24 | def pytest_configure(config): 25 | config.addinivalue_line( 26 | "markers", "canary: mark test to also run in canary tests" 27 | ) 28 | config.addinivalue_line( 29 | "markers", "service(arg): mark test associated with a given service" 30 | ) 31 | config.addinivalue_line( 32 | "markers", "slow: mark test as slow to run" 33 | ) 34 | 35 | def pytest_collection_modifyitems(config, items): 36 | if config.getoption("--runslow"): 37 | return 38 | skip_slow = pytest.mark.skip(reason="need --runslow option to run") 39 | for item in items: 40 | if "slow" in item.keywords: 41 | item.add_marker(skip_slow) 42 | 43 | @pytest.fixture(scope='class') 44 | def k8s_client(): 45 | return k8s._get_k8s_api_client() 46 | 47 | @pytest.fixture(scope="module") 48 | def ecr_client(): 49 | return boto3.client("ecr") 50 | -------------------------------------------------------------------------------- /test/e2e/fixtures.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | 14 | """Fixtures common to all ECR controller tests""" 15 | 16 | import dataclasses 17 | 18 | from acktest.k8s import resource as k8s 19 | import pytest 20 | from typing import Dict 21 | import os 22 | import distutils.util as util 23 | from typing import Dict 24 | from kubernetes import config, client 25 | from kubernetes.client.api_client import ApiClient 26 | 27 | @dataclasses.dataclass 28 | class ConfigMap: 29 | namespace: str 30 | name: str 31 | data: Dict[str, int] 32 | 33 | def create_config_map(namespace: str, 34 | name: str, 35 | data: dict, 36 | ): 37 | _api_client = _get_k8s_api_client() 38 | body = client.V1Secret() 39 | body.api_version = 'v1' 40 | body.data = data 41 | body.kind = 'ConfigMap' 42 | body.metadata = { 43 | 'name': name, 44 | } 45 | body = _api_client.sanitize_for_serialization(body) 46 | client.CoreV1Api(_api_client).create_namespaced_config_map(namespace, body) 47 | 48 | def _get_k8s_api_client() -> ApiClient: 49 | # Create new client every time to avoid token refresh issues 50 | # https://github.com/kubernetes-client/python/issues/741 51 | # https://github.com/kubernetes-client/python-base/issues/125 52 | if bool(util.strtobool(os.environ.get('LOAD_IN_CLUSTER_KUBECONFIG', 'false'))): 53 | config.load_incluster_config() 54 | return ApiClient() 55 | return config.new_client_from_config() -------------------------------------------------------------------------------- /test/e2e/replacement_values.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | 14 | """Stores the values used by each of the integration tests for replacing the 15 | ECR-specific test variables. 16 | """ 17 | 18 | REPLACEMENT_VALUES = { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /test/e2e/requirements.txt: -------------------------------------------------------------------------------- 1 | acktest @ git+https://github.com/aws-controllers-k8s/test-infra.git@38ce32256cc2552ab54e190cc8a8618e93af9e0c 2 | -------------------------------------------------------------------------------- /test/e2e/resources/pull_through_cache_rule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ecr.services.k8s.aws/v1alpha1 2 | kind: PullThroughCacheRule 3 | metadata: 4 | name: $NAME 5 | spec: 6 | registryID: "$REGISTRY_ID" 7 | ecrRepositoryPrefix: $ECR_REPOSITORY_PREFIX 8 | upstreamRegistryURL: $UPSTREAM_REGISTRY_URL -------------------------------------------------------------------------------- /test/e2e/resources/repository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ecr.services.k8s.aws/v1alpha1 2 | kind: Repository 3 | metadata: 4 | name: $REPOSITORY_NAME 5 | spec: 6 | name: $REPOSITORY_NAME 7 | imageScanningConfiguration: 8 | scanOnPush: false 9 | imageTagMutability: MUTABLE -------------------------------------------------------------------------------- /test/e2e/resources/repository_all_fields.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ecr.services.k8s.aws/v1alpha1 2 | kind: Repository 3 | metadata: 4 | name: $REPOSITORY_NAME 5 | spec: 6 | name: $REPOSITORY_NAME 7 | registryID: '$REGISTRY_ID' 8 | imageScanningConfiguration: 9 | scanOnPush: false 10 | imageTagMutability: MUTABLE 11 | encryptionConfiguration: 12 | encryptionType: AES256 13 | policy: '$REPOSITORY_POLICY' 14 | lifecyclePolicy: '$REPOSITORY_LIFECYCLE_POLICY' -------------------------------------------------------------------------------- /test/e2e/resources/repository_carm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ecr.services.k8s.aws/v1alpha1 2 | kind: Repository 3 | metadata: 4 | name: $REPOSITORY_NAME 5 | namespace: $NAMESPACE 6 | spec: 7 | name: $REPOSITORY_NAME 8 | registryID: $REGISTRY_ID -------------------------------------------------------------------------------- /test/e2e/resources/repository_lifecycle_policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ecr.services.k8s.aws/v1alpha1 2 | kind: Repository 3 | metadata: 4 | name: $REPOSITORY_NAME 5 | spec: 6 | name: $REPOSITORY_NAME 7 | imageScanningConfiguration: 8 | scanOnPush: false 9 | imageTagMutability: MUTABLE 10 | lifecyclePolicy: '{"rules":[{"rulePriority":1,"description":"Expire images older than 14 days","selection":{"tagStatus":"untagged","countType":"sinceImagePushed","countUnit":"days","countNumber":14},"action":{"type":"expire"}}]}' -------------------------------------------------------------------------------- /test/e2e/resources/repository_policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ecr.services.k8s.aws/v1alpha1 2 | kind: Repository 3 | metadata: 4 | name: $REPOSITORY_NAME 5 | spec: 6 | name: $REPOSITORY_NAME 7 | policy: '$REPOSITORY_POLICY' -------------------------------------------------------------------------------- /test/e2e/resources/repository_region.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ecr.services.k8s.aws/v1alpha1 2 | kind: Repository 3 | metadata: 4 | name: $REPOSITORY_NAME 5 | namespace: $NAMESPACE 6 | spec: 7 | name: $REPOSITORY_NAME -------------------------------------------------------------------------------- /test/e2e/service_bootstrap.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | """Bootstraps the resources required to run the ECR integration tests. 14 | """ 15 | 16 | import logging 17 | from time import sleep 18 | 19 | from e2e import bootstrap_directory 20 | from e2e.bootstrap_resources import BootstrapResources 21 | 22 | def service_bootstrap() -> dict: 23 | logging.getLogger().setLevel(logging.INFO) 24 | 25 | resources = BootstrapResources() 26 | return resources 27 | 28 | if __name__ == "__main__": 29 | config = service_bootstrap() 30 | config.serialize(bootstrap_directory) -------------------------------------------------------------------------------- /test/e2e/service_cleanup.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | """Cleans up the resources created by the Application Auto Scaling bootstrapping process. 14 | """ 15 | 16 | import logging 17 | 18 | from acktest.bootstrapping import Resources 19 | 20 | from e2e import bootstrap_directory 21 | 22 | def service_cleanup(): 23 | logging.getLogger().setLevel(logging.INFO) 24 | resources = Resources.deserialize(bootstrap_directory) 25 | resources.cleanup() 26 | 27 | if __name__ == "__main__": 28 | service_cleanup() -------------------------------------------------------------------------------- /test/e2e/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /test/e2e/tests/test_cross_account.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | 14 | """Integration tests for ECR Cross Account Resource Management. 15 | Ideally we want these tests to be in the ACK runtime, but we don't have a way 16 | to run them there yet. So we'll run them here for now. 17 | """ 18 | 19 | import pytest 20 | import time 21 | import logging 22 | import boto3 23 | import os 24 | 25 | from acktest.resources import random_suffix_name 26 | from acktest.k8s import resource as k8s 27 | from e2e import service_marker, CRD_GROUP, CRD_VERSION, load_ecr_resource 28 | from e2e.replacement_values import REPLACEMENT_VALUES 29 | from e2e.fixtures import create_config_map 30 | 31 | RESOURCE_PLURAL = "repositories" 32 | 33 | CREATE_WAIT_AFTER_SECONDS = 10 34 | UPDATE_WAIT_AFTER_SECONDS = 10 35 | DELETE_WAIT_AFTER_SECONDS = 10 36 | 37 | TESTING_NAMESPACE = "carm-testing" 38 | ACK_SYSTEM_NAMESPACE = "ack-system" 39 | TESTING_ACCOUNT = "637423602339" 40 | TESTTING_ASSUME_ROLE = "arn:aws:iam::637423602339:role/ack-carm-role-DO-NOT-DELETE" 41 | 42 | @service_marker 43 | @pytest.mark.canary 44 | class TestCARM: 45 | def get_repository(self, repository_name: str) -> dict: 46 | ecr_client = boto3.client( 47 | "ecr", 48 | aws_access_key_id=os.environ["CARM_AWS_ACCESS_KEY_ID"], 49 | aws_secret_access_key=os.environ["CARM_AWS_SECRET_ACCESS_KEY"], 50 | aws_session_token=os.environ["CARM_AWS_SESSION_TOKEN"], 51 | ) 52 | try: 53 | resp = ecr_client.describe_repositories( 54 | repositoryNames=[repository_name] 55 | ) 56 | except Exception as e: 57 | logging.debug(e) 58 | return None 59 | 60 | repositories = resp["repositories"] 61 | for repository in repositories: 62 | if repository["repositoryName"] == repository_name: 63 | return repository 64 | 65 | return None 66 | 67 | def repository_exists(self, repository_name: str) -> bool: 68 | return self.get_repository(repository_name) is not None 69 | 70 | def test_basic_repository(self): 71 | create_config_map( 72 | ACK_SYSTEM_NAMESPACE, 73 | "ack-role-account-map", 74 | { 75 | TESTING_ACCOUNT: TESTTING_ASSUME_ROLE, 76 | }, 77 | ) 78 | k8s.create_k8s_namespace( 79 | TESTING_NAMESPACE, 80 | annotations={ 81 | "services.k8s.aws/owner-account-id": TESTING_ACCOUNT, 82 | } 83 | ) 84 | 85 | time.sleep(CREATE_WAIT_AFTER_SECONDS) 86 | 87 | resource_name = random_suffix_name("ecr-carm-repository", 24) 88 | 89 | replacements = REPLACEMENT_VALUES.copy() 90 | replacements["REPOSITORY_NAME"] = resource_name 91 | replacements["NAMESPACE"] = TESTING_NAMESPACE 92 | replacements["REGISTRY_ID"] = '"'+TESTING_ACCOUNT +'"' 93 | # Load ECR CR 94 | resource_data = load_ecr_resource( 95 | "repository_carm", 96 | additional_replacements=replacements, 97 | ) 98 | logging.debug(resource_data) 99 | 100 | # Create k8s resource 101 | ref = k8s.CustomResourceReference( 102 | CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, 103 | resource_name, namespace=TESTING_NAMESPACE, 104 | ) 105 | 106 | k8s.create_custom_resource(ref, resource_data) 107 | cr = k8s.wait_resource_consumed_by_controller(ref) 108 | assert cr is not None 109 | assert k8s.get_resource_exists(ref) 110 | 111 | time.sleep(CREATE_WAIT_AFTER_SECONDS) 112 | 113 | # Check ECR repository exists 114 | exists = self.repository_exists(resource_name) 115 | assert exists 116 | 117 | # Delete k8s resource 118 | _, deleted = k8s.delete_custom_resource(ref) 119 | assert deleted is True 120 | 121 | time.sleep(DELETE_WAIT_AFTER_SECONDS) 122 | 123 | # Check ECR repository doesn't exists 124 | exists = self.repository_exists(resource_name) 125 | assert not exists -------------------------------------------------------------------------------- /test/e2e/tests/test_cross_region.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | 14 | """Integration tests for ECR Cross Account Resource Management. 15 | Ideally we want these tests to be in the ACK runtime, but we don't have a way 16 | to run them there yet. So we'll run them here for now. 17 | """ 18 | 19 | import pytest 20 | import time 21 | import logging 22 | import boto3 23 | 24 | from acktest.resources import random_suffix_name 25 | from acktest.k8s import resource as k8s 26 | from e2e import service_marker, CRD_GROUP, CRD_VERSION, load_ecr_resource 27 | from e2e.replacement_values import REPLACEMENT_VALUES 28 | 29 | RESOURCE_PLURAL = "repositories" 30 | 31 | CREATE_WAIT_AFTER_SECONDS = 10 32 | UPDATE_WAIT_AFTER_SECONDS = 10 33 | DELETE_WAIT_AFTER_SECONDS = 10 34 | 35 | TESTING_NAMESPACE = "cross-region-testing" 36 | TESTING_REGION = "eu-central-1" 37 | 38 | @service_marker 39 | @pytest.mark.canary 40 | class TestCrossRegion: 41 | def get_repository(self, repository_name: str) -> dict: 42 | ecr_client = boto3.client( 43 | "ecr", 44 | region_name=TESTING_REGION, 45 | ) 46 | try: 47 | resp = ecr_client.describe_repositories( 48 | repositoryNames=[repository_name] 49 | ) 50 | except Exception as e: 51 | logging.debug(e) 52 | return None 53 | 54 | repositories = resp["repositories"] 55 | for repository in repositories: 56 | if repository["repositoryName"] == repository_name: 57 | return repository 58 | 59 | return None 60 | 61 | def repository_exists(self, repository_name: str) -> bool: 62 | return self.get_repository(repository_name) is not None 63 | 64 | def test_basic_repository(self): 65 | k8s.create_k8s_namespace( 66 | TESTING_NAMESPACE, 67 | annotations={ 68 | "services.k8s.aws/default-region": TESTING_REGION, 69 | } 70 | ) 71 | 72 | time.sleep(CREATE_WAIT_AFTER_SECONDS) 73 | 74 | resource_name = random_suffix_name("ecr-cross-region", 24) 75 | 76 | replacements = REPLACEMENT_VALUES.copy() 77 | replacements["REPOSITORY_NAME"] = resource_name 78 | replacements["NAMESPACE"] = TESTING_NAMESPACE 79 | # Load ECR CR 80 | resource_data = load_ecr_resource( 81 | "repository_region", 82 | additional_replacements=replacements, 83 | ) 84 | logging.debug(resource_data) 85 | 86 | # Create k8s resource 87 | ref = k8s.CustomResourceReference( 88 | CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, 89 | resource_name, namespace=TESTING_NAMESPACE, 90 | ) 91 | 92 | k8s.create_custom_resource(ref, resource_data) 93 | cr = k8s.wait_resource_consumed_by_controller(ref) 94 | assert cr is not None 95 | assert k8s.get_resource_exists(ref) 96 | 97 | time.sleep(CREATE_WAIT_AFTER_SECONDS) 98 | 99 | # Check ECR repository exists 100 | exists = self.repository_exists(resource_name) 101 | assert exists 102 | 103 | # Delete k8s resource 104 | _, deleted = k8s.delete_custom_resource(ref) 105 | assert deleted is True 106 | 107 | time.sleep(DELETE_WAIT_AFTER_SECONDS) 108 | 109 | # Check ECR repository doesn't exists 110 | exists = self.repository_exists(resource_name) 111 | assert not exists -------------------------------------------------------------------------------- /test/e2e/tests/test_pull_through_cache_rule.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You may 4 | # not use this file except in compliance with the License. A copy of the 5 | # License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is distributed 10 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | # express or implied. See the License for the specific language governing 12 | # permissions and limitations under the License. 13 | 14 | """Integration tests for the ECR Pull Through Cache Rule API. 15 | """ 16 | 17 | import pytest 18 | import time 19 | import logging 20 | from typing import Dict, Tuple 21 | 22 | from acktest.resources import random_suffix_name 23 | from acktest.k8s import resource as k8s 24 | from e2e import service_marker, CRD_GROUP, CRD_VERSION, load_ecr_resource 25 | from e2e.replacement_values import REPLACEMENT_VALUES 26 | from e2e.bootstrap_resources import BootstrapResources, get_bootstrap_resources 27 | 28 | RESOURCE_PLURAL = "pullthroughcacherules" 29 | 30 | CREATE_WAIT_AFTER_SECONDS = 10 31 | DELETE_WAIT_AFTER_SECONDS = 10 32 | 33 | @service_marker 34 | @pytest.mark.canary 35 | class TestPullThroughCacheRule: 36 | def get_pull_through_cache_rule(self, ecr_client, registry_id: str, ecr_repository_prefix: str) -> dict: 37 | try: 38 | resp = ecr_client.describe_pull_through_cache_rules( 39 | registryId=registry_id, 40 | ecrRepositoryPrefixes=[ecr_repository_prefix] 41 | ) 42 | except Exception as e: 43 | logging.debug(e) 44 | return None 45 | 46 | pull_through_cache_rules = resp["pullThroughCacheRules"] 47 | for rule in pull_through_cache_rules: 48 | if rule["registryId"] == registry_id and rule["ecrRepositoryPrefix"] == ecr_repository_prefix: 49 | return rule 50 | return None 51 | 52 | def get_registry_id(self, ecr_client) -> str: 53 | try: 54 | resp = ecr_client.describe_registry() 55 | return resp["registryId"] 56 | except Exception as e: 57 | logging.debug(e) 58 | return "" 59 | 60 | 61 | def pull_through_cache_rule_exists(self, ecr_client, registry_id: str, ecr_repository_prefix:str) -> bool: 62 | return self.get_pull_through_cache_rule(ecr_client, registry_id, ecr_repository_prefix) is not None 63 | 64 | def test_basic_pull_through_cache_rule(self, ecr_client): 65 | resource_name = random_suffix_name("ecr-ptcr", 24) 66 | registry_id = self.get_registry_id(ecr_client) 67 | ecr_repository_prefix = "ecr-public" 68 | 69 | replacements = REPLACEMENT_VALUES.copy() 70 | replacements["NAME"] = resource_name 71 | replacements["REGISTRY_ID"] = registry_id 72 | replacements["ECR_REPOSITORY_PREFIX"] = ecr_repository_prefix 73 | replacements["UPSTREAM_REGISTRY_URL"] = "public.ecr.aws" 74 | # Load ECR CR 75 | resource_data = load_ecr_resource( 76 | "pull_through_cache_rule", 77 | additional_replacements=replacements, 78 | ) 79 | logging.debug(resource_data) 80 | 81 | # Create k8s resource 82 | ref = k8s.CustomResourceReference( 83 | CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, 84 | resource_name, namespace="default", 85 | ) 86 | k8s.create_custom_resource(ref, resource_data) 87 | cr = k8s.wait_resource_consumed_by_controller(ref) 88 | 89 | assert cr is not None 90 | assert k8s.get_resource_exists(ref) 91 | 92 | time.sleep(CREATE_WAIT_AFTER_SECONDS) 93 | 94 | # Get latest PTCR CR 95 | cr = k8s.wait_resource_consumed_by_controller(ref) 96 | 97 | # Check ECR PTCR exists 98 | exists = self.pull_through_cache_rule_exists(ecr_client, registry_id, ecr_repository_prefix) 99 | assert exists 100 | 101 | # Delete k8s resource 102 | _, deleted = k8s.delete_custom_resource(ref) 103 | assert deleted is True 104 | 105 | time.sleep(DELETE_WAIT_AFTER_SECONDS) 106 | 107 | # Check ECR PTCR doesn't exists 108 | exists = self.pull_through_cache_rule_exists(ecr_client, registry_id, ecr_repository_prefix) 109 | assert not exists 110 | --------------------------------------------------------------------------------