.codebuild/000077500000000000000000000000001443273566100131015ustar00rootroot00000000000000.codebuild/buildspec.yml000066400000000000000000000010241443273566100155730ustar00rootroot00000000000000version: 0.2 phases: install: commands: - chmod +x -R scripts - ./scripts/hack/codepipeline-git-commit.sh - ./scripts/hack/symlink-gopath-codebuild.sh - cd /go/src/github.com/awslabs/amazon-ecr-credential-helper pre_build: commands: - echo "Starting tests..." - make test build: commands: - echo "Starting build..." - make all-variants post_build: commands: - echo "Build completed on $(date)" artifacts: files: - '**/*' base-directory: 'bin' .codebuild/source-archive.yml000066400000000000000000000006441443273566100165470ustar00rootroot00000000000000version: 0.2 phases: build: commands: - ./scripts/hack/codepipeline-source-archive.sh - ./scripts/hack/version-changelog.sh | tee archive/VERSION_CHANGELOG.md post_build: commands: - echo "Archive completed on $(date)" artifacts: files: - 'release.tar.gz' - 'release-novendor.tar.gz' - 'VERSION' - 'GITCOMMIT_SHA' - 'VERSION_CHANGELOG.md' base-directory: 'archive' .github/000077500000000000000000000000001443273566100124315ustar00rootroot00000000000000.github/CODEOWNERS000066400000000000000000000000411443273566100140170ustar00rootroot00000000000000* @awslabs/runtime @awslabs/ecr .github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000002511443273566100162300ustar00rootroot00000000000000*Issue #, if available:* *Description of changes:* By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. .github/dependabot.yml000066400000000000000000000001571443273566100152640ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: gomod directory: /ecr-login schedule: interval: daily .github/workflows/000077500000000000000000000000001443273566100144665ustar00rootroot00000000000000.github/workflows/build.yaml000066400000000000000000000020641443273566100164530ustar00rootroot00000000000000name: Build on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: strategy: matrix: go: ['1.15', '1.16', '1.17', '1.18'] # Intentionally use specific versions instead of "latest" to # make this build reproducible. os: ['ubuntu-22.04', 'macos-12'] # Build all variants regardless of failures fail-fast: false name: ${{ matrix.os }} / Go ${{ matrix.go }} runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} - run: make get-deps if: ${{ matrix.go >= '1.17' }} # Apple Silicon is not supported by Go < 1.16. # https://go.dev/blog/go1.16 - name: Cross-compile all variants run: make all-variants if: ${{ matrix.go >= '1.16' }} - name: Cross-compile all variants except for Apple Silicon run: make linux-amd64 linux-arm64 darwin-amd64 windows-amd64 if: ${{ matrix.go < '1.16' }} - run: make test .github/workflows/codeql.yml000066400000000000000000000014421443273566100164610ustar00rootroot00000000000000name: "CodeQL Scan" on: push: branches: [ "main" ] pull_request: branches: [ "main" ] schedule: - cron: '25 21 * * 5' jobs: analyze: name: Analyze runs-on: ubuntu-22.04 permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] steps: - name: Checkout repository uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - name: Build run: make build - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: category: "/language:${{matrix.language}}" .gitignore000066400000000000000000000000431443273566100130560ustar00rootroot00000000000000/bin/ GITCOMMIT_SHA *.tar.gz *.tar CHANGELOG.md000066400000000000000000000061111443273566100127010ustar00rootroot00000000000000# 0.7.1 **Note: v0.7.1 is functionally equivalent to v0.7.0. We have decided to create a duplicate release to reflect a more accurate changelog, since our v0.7.0 release did not contain any direct/indirect security patches.** * Feature - Allow callers to set log output. ([#309](https://github.com/awslabs/amazon-ecr-credential-helper/pull/309) and [#312](https://github.com/awslabs/amazon-ecr-credential-helper/pull/312)) * Upgrade dependencies for bug fixes. # 0.7.0 * Feature - Allow callers to set log output. ([#309](https://github.com/awslabs/amazon-ecr-credential-helper/pull/309) and [#312](https://github.com/awslabs/amazon-ecr-credential-helper/pull/312)) * Upgrade dependencies for bug fixes and security patches. # 0.6.0 * Feature - Added support for [AWS SSO](https://aws.amazon.com/single-sign-on/) ([#229](https://github.com/awslabs/amazon-ecr-credential-helper/issues/229)) * Feature - Added support to assume roles via EC2 instance metadata. ([#282](https://github.com/awslabs/amazon-ecr-credential-helper/issues/282)) * Feature - Added support for [Apple Silicon](https://go.dev/doc/go1.16#darwin) ([#291](https://github.com/awslabs/amazon-ecr-credential-helper/pull/291)) * Enhancement - The AWS shared config file (`~/.aws/config`) is now always enabled. (`AWS_SDK_LOAD_CONFIG` environment variable is no longer supported) ([#282](https://github.com/awslabs/amazon-ecr-credential-helper/issues/282)) # 0.5.0 * Feature - Added support for [ECR Public](https://gallery.ecr.aws) ([#253](https://github.com/awslabs/amazon-ecr-credential-helper/pull/253)) * Feature - Added support for [EC2 IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html) ([#240](https://github.com/awslabs/amazon-ecr-credential-helper/pull/240)) * Enhancement - The AWS shared config file (`~/.aws/config`) is now enabled by default. This can be disabled by setting the environment variable `AWS_SDK_LOAD_CONFIG` to `false` ([#201](https://github.com/awslabs/amazon-ecr-credential-helper/pull/201)) * Bug - Fixed an issue where output from a `credential_process` was sometimes too big for the default buffer ([#240](https://github.com/awslabs/amazon-ecr-credential-helper/pull/240)) # 0.4.0 * Feature - Added support for chaining assumed roles in the shared config file (`~/.aws/config`) defined by `source_profile` and `credential_source` ([#177](https://github.com/awslabs/amazon-ecr-credential-helper/pull/177)) * Feature - Added support for Web Identities and [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) with Kubernetes ([#183](https://github.com/awslabs/amazon-ecr-credential-helper/pull/183)) * Bug - Fixed the `make docker` target when the credential helper git repository is used as a git submodule ([#184](https://github.com/awslabs/amazon-ecr-credential-helper/issues/184)) # 0.3.1 * Bug - Log directory is now automatically created when the helper runs # 0.3.0 * Feature - Added support for PrivateLink endpoints # 0.2.0 * Feature - Added support for FIPS endpoints # 0.1.0 Initial release CODE_OF_CONDUCT.md000066400000000000000000000004671443273566100136770ustar00rootroot00000000000000## Code of Conduct This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. CONTRIBUTING.md000066400000000000000000000070671443273566100133340ustar00rootroot00000000000000# Contributing Guidelines Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional documentation, we greatly value feedback and contributions from our community. Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. ## Reporting Bugs/Feature Requests We welcome you to use the GitHub issue tracker to report bugs or suggest features. When filing an issue, please check [existing open](https://github.com/awslabs/amazon-ecr-credential-helper/issues), or [recently closed](https://github.com/awslabs/amazon-ecr-credential-helper/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: * A reproducible test case or series of steps * The version of our code being used * Any modifications you've made relevant to the bug * Anything unusual about your environment or deployment ## Contributing via Pull Requests Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 1. You are working against the latest source on the *master* branch. 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. To send us a pull request, please: 1. Fork the repository. 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 3. Ensure local tests pass. 4. Commit to your fork using clear commit messages. 5. Send us a pull request, answering any default questions in the pull request interface. 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). ## Finding contributions to work on Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/amazon-ecr-credential-helper/labels/help%20wanted) issues is a great place to start. ## Code of Conduct This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. ## Security issue notifications If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. ## Licensing See the [LICENSE](https://github.com/awslabs/amazon-ecr-credential-helper/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. Dockerfile000066400000000000000000000012361443273566100130650ustar00rootroot00000000000000# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. FROM golang:1.15 WORKDIR /go/src/github.com/awslabs/amazon-ecr-credential-helper COPY . . CMD make LICENSE000066400000000000000000000244621443273566100121060ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. *** Note: Other license terms may apply to certain, identified software files contained within or distributed with the accompanying software if such terms are included in the directory containing the accompanying software. Such other license terms will then apply in lieu of the terms of the software license above. Makefile000066400000000000000000000067661443273566100125500ustar00rootroot00000000000000# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. ROOT := $(shell pwd) all: build SOURCEDIR=./ecr-login SOURCES := $(shell find $(SOURCEDIR) -name '*.go') VERSION := $(shell cat VERSION) GITFILES := $(shell test -d .git && find ".git/" -type f) BINARY_NAME=docker-credential-ecr-login LOCAL_BINARY=bin/local/$(BINARY_NAME) LINUX_AMD64_BINARY=bin/linux-amd64/$(BINARY_NAME) LINUX_ARM64_BINARY=bin/linux-arm64/$(BINARY_NAME) DARWIN_AMD64_BINARY=bin/darwin-amd64/$(BINARY_NAME) DARWIN_ARM64_BINARY=bin/darwin-arm64/$(BINARY_NAME) WINDOWS_AMD64_BINARY=bin/windows-amd64/$(BINARY_NAME).exe .PHONY: docker docker: Dockerfile GITCOMMIT_SHA mkdir -p bin docker run --rm \ -e TARGET_GOOS=$(TARGET_GOOS) \ -e TARGET_GOARCH=$(TARGET_GOARCH) \ -v '$(shell pwd)/bin':/go/src/github.com/awslabs/amazon-ecr-credential-helper/bin \ $(shell docker build -q .) .PHONY: build build: $(LOCAL_BINARY) $(LOCAL_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_binary.sh ./bin/local $(VERSION) $(shell cat GITCOMMIT_SHA) @echo "Built ecr-login" .PHONY: test test: cd $(SOURCEDIR) && go test -v -timeout 30s -short -cover ./... .PHONY: all-variants all-variants: linux-amd64 linux-arm64 darwin-amd64 darwin-arm64 windows-amd64 .PHONY: linux-amd64 linux-amd64: $(LINUX_AMD64_BINARY) $(LINUX_AMD64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh linux amd64 $(VERSION) $(shell cat GITCOMMIT_SHA) .PHONY: linux-arm64 linux-arm64: $(LINUX_ARM64_BINARY) $(LINUX_ARM64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh linux arm64 $(VERSION) $(shell cat GITCOMMIT_SHA) .PHONY: darwin-amd64 darwin-amd64: $(DARWIN_AMD64_BINARY) $(DARWIN_AMD64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh darwin amd64 $(VERSION) $(shell cat GITCOMMIT_SHA) .PHONY: darwin-arm64 darwin-arm64: $(DARWIN_ARM64_BINARY) $(DARWIN_ARM64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh darwin arm64 $(VERSION) $(shell cat GITCOMMIT_SHA) .PHONY: windows-amd64 windows-amd64: $(WINDOWS_AMD64_BINARY) $(WINDOWS_AMD64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh windows amd64 $(VERSION) $(shell cat GITCOMMIT_SHA) @mv ./bin/windows-amd64/$(BINARY_NAME) ./$(WINDOWS_AMD64_BINARY) GITCOMMIT_SHA: $(GITFILES) git rev-parse --short=7 HEAD > GITCOMMIT_SHA release.tar: GITCOMMIT_SHA git archive -o release.tar HEAD tar -f release.tar --append GITCOMMIT_SHA --owner 0 --group 0 .PHONY: release-tarball release-tarball: release.tar.gz release.tar.gz: release.tar gzip release.tar .PHONY: release-tarball-no-vendor release-tarball-no-vendor: release-novendor.tar.gz release-novendor.tar.gz: release.tar mv release.tar release-novendor.tar tar -f release-novendor.tar --wildcards --delete 'ecr-login/vendor/*' gzip release-novendor.tar .PHONY: gogenerate gogenerate: ./scripts/gogenerate .PHONY: get-deps get-deps: go install golang.org/x/tools/cmd/goimports@698251aaa532d49ac69d2c416b0241afb2f65ea5 .PHONY: clean clean: - rm -rf ./bin - rm -f GITCOMMIT_SHA - rm -f release.tar.gz - rm -f release-novendor.tar.gz NOTICE000066400000000000000000000001451443273566100117750ustar00rootroot00000000000000Amazon ECR Credential Helper Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. README.md000066400000000000000000000266721443273566100123650ustar00rootroot00000000000000# Amazon ECR Docker Credential Helper ![Amazon ECR logo](docs/ecr.png "Amazon ECR") [![Build](https://github.com/awslabs/amazon-ecr-credential-helper/actions/workflows/build.yaml/badge.svg)](https://github.com/awslabs/amazon-ecr-credential-helper/actions/workflows/build.yaml) [![Go Report Card](https://goreportcard.com/badge/github.com/awslabs/amazon-ecr-credential-helper)](https://goreportcard.com/report/github.com/awslabs/amazon-ecr-credential-helper) [![latest packaged version(s)](https://repology.org/badge/latest-versions/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) The Amazon ECR Docker Credential Helper is a [credential helper](https://github.com/docker/docker-credential-helpers) for the Docker daemon that makes it easier to use [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/). ## Prerequisites You must have at least Docker 1.11 installed on your system. You also must have AWS credentials available. See the [AWS credentials section](#aws-credentials) for details on how to use different AWS credentials. ## Installing ### Amazon Linux 2 You can install the Amazon ECR Credential Helper from the [`docker` or `ecs` extras](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-ami-basics.html#extras-library). ```bash $ sudo amazon-linux-extras enable docker $ sudo yum install amazon-ecr-credential-helper ``` Once you have installed the credential helper, see the [Configuration section](#Configuration) for instructions on how to configure Docker to work with the helper. ### Mac OS A community-maintained Homebrew formula is available in the core tap. [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) ```bash $ brew install docker-credential-helper-ecr ``` On macOS, another community-maintained installation method is to use MacPorts. [![MacPorts package](https://repology.org/badge/version-for-repo/macports/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) ```bash $ sudo port install docker-credential-helper-ecr ``` Once you have installed the credential helper, see the [Configuration section](#Configuration) for instructions on how to configure Docker to work with the helper. ### Debian Buster (and future versions) You can install the Amazon ECR Credential Helper from the Debian Buster archives. This package will also be included in future releases of Debian. [![Debian 10 package](https://repology.org/badge/version-for-repo/debian_10/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) [![Debian 11 package](https://repology.org/badge/version-for-repo/debian_11/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) [![Debian 12 package](https://repology.org/badge/version-for-repo/debian_12/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) [![Debian Unstable package](https://repology.org/badge/version-for-repo/debian_unstable/amazon-ecr-credential-helper.svg)](https://repology.org/metapackage/amazon-ecr-credential-helper/versions) ```bash $ sudo apt update $ sudo apt install amazon-ecr-credential-helper ``` Once you have installed the credential helper, see the [Configuration section](#Configuration) for instructions on how to configure Docker to work with the helper. ### Ubuntu 19.04 Disco Dingo and newer You can install the Amazon ECR Credential Helper from the Ubuntu 19.04 Disco Dingo (and newer) archives. [![Ubuntu 20.04 package](https://repology.org/badge/version-for-repo/ubuntu_20_04/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) [![Ubuntu 22.04 package](https://repology.org/badge/version-for-repo/ubuntu_22_04/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) ```bash $ sudo apt update $ sudo apt install amazon-ecr-credential-helper ``` Once you have installed the credential helper, see the [Configuration section](#Configuration) for instructions on how to configure Docker to work with the helper. ### Arch Linux A community-maintained package is available in the Arch User Repository. [![AUR package](https://repology.org/badge/version-for-repo/aur/amazon-ecr-credential-helper.svg)](https://repology.org/metapackage/amazon-ecr-credential-helper/versions) ```bash $ git clone https://aur.archlinux.org/amazon-ecr-credential-helper.git $ cd amazon-ecr-credential-helper $ makepkg -si ``` Once you have installed the credential helper, see the [Configuration section](#Configuration) for instructions on how to configure Docker to work with the helper. ### From Source To build and install the Amazon ECR Docker Credential Helper, we suggest Go 1.15 or later, `git` and `make` installed on your system. If you just installed Go, make sure you also have added it to your PATH or Environment Vars (Windows). For example: ``` $ export GOPATH=$HOME/go $ export PATH=$PATH:$GOPATH/bin ``` Or in Windows: ``` setx GOPATH %USERPROFILE%\go ;%USERPROFILE%\go\bin ``` If you haven't defined the PATH, the command below will fail silently, and running `docker-credential-ecr-login` will output: `command not found` You can install this via the `go` command line tool. For go version 1.16 and newer run : ``` go install github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login@latest ``` or with an older version of go run : ``` go get -u github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login ``` If you already have Docker environment, just clone this repository anywhere and run `make docker`. This command builds the binary with Go inside the Docker container and output it to local directory. With `TARGET_GOOS` environment variable, you can also cross compile the binary. Once you have installed the credential helper, see the [Configuration section](#Configuration) for instructions on how to configure Docker to work with the helper. ## Configuration ### Docker Place the `docker-credential-ecr-login` binary on your `PATH` and set the contents of your `~/.docker/config.json` file to be: ```json { "credsStore": "ecr-login" } ``` This configures the Docker daemon to use the credential helper for all Amazon ECR registries. The Amazon ECR Docker Credential Helper can be used alongside your existing docker login authentication tokens: ```json { "credsStore": "ecr-login", "auths": { "https://index.docker.io/v1/": { "auth": [docker.io-auth-token] }, "registry.gitlab.com": { "auth": [gitlab-auth-token] }, } } ``` With Docker 1.13.0 or greater, you can configure Docker to use different credential helpers for different ECR registries. To use this credential helper for a specific ECR registry, create a `credHelpers` section with the URI of your ECR registry: ```json { "credHelpers": { "public.ecr.aws": "ecr-login", ".dkr.ecr..amazonaws.com": "ecr-login" } } ``` This is useful if you use `docker` to operate on registries that use different authentication credentials. ### AWS credentials The Amazon ECR Docker Credential Helper allows you to use AWS credentials stored in different locations. Standard ones include: * The shared credentials file (`~/.aws/credentials`) * The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables * An [IAM role for an Amazon ECS task](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) * An [IAM role for Amazon EC2](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html) To use credentials associated with a different named profile in the shared credentials file (`~/.aws/credentials`), you may set the `AWS_PROFILE` environment variable. The Amazon ECR Docker Credential Helper reads and supports some configuration options specified in the AWS shared configuration file (`~/.aws/config`). To disable these options, you must set the `AWS_SDK_LOAD_CONFIG` environment variable to `false`. The supported options include: * Assumed roles specified with `role_arn` and `source_profile` * External credential processes specified with `credential_process` * Web Identities like [IAM Roles for Service Accounts in Kubernetes](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) (*Note: Kubernetes users using containers with a non-root user may encounter permission issues described in [this bug](https://github.com/kubernetes-sigs/external-dns/pull/1185) and may need to employ a workaround adjusting the Kubernetes `securityContext`.*) The Amazon ECR Docker Credential Helper uses the same credentials as the AWS CLI and the AWS SDKs. For more information about configuring AWS credentials, see [Configuration and Credential Files](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files) in the *AWS Command Line Interface User Guide*. The credentials must have a policy applied that [allows access to Amazon ECR](http://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html). ### Amazon ECR Docker Credential Helper | Environment Variable | Sample Value | Description | | --------------------- | ------------- | ------------------------------------------------------------------ | | AWS_ECR_DISABLE_CACHE | true | Disables the local file auth cache if set to a non-empty value | | AWS_ECR_CACHE_DIR | ~/.ecr | Specifies the local file auth cache directory location | ## Usage `docker pull 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repository:my-tag` `docker push 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repository:my-tag` `docker pull public.ecr.aws/amazonlinux/amazonlinux:latest` If you have configured additional profiles for use with the AWS CLI, you can use those profiles by specifying the `AWS_PROFILE` environment variable when invoking `docker`. For example: `AWS_PROFILE=myprofile docker pull 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repository:my-tag` There is no need to use `docker login` or `docker logout`. ## Troubleshooting If you have previously authenticated with an ECR repository by using the `docker login` command manually then Docker may have stored an auth token which has since expired. Docker will continue to attempt to use that cached auth token instead of utilizing the credential helper. You must explicitly remove the previously cached expired token using `docker logout 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repository`. After that Docker will start utilizing the ECR credential helper to fetch fresh credentials, and you will no longer need to use `docker login` or `docker logout`. Logs from the Amazon ECR Docker Credential Helper are stored in `~/.ecr/log`. For more information about Amazon ECR, see the the [Amazon Elastic Container Registry User Guide](http://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html). ## Security disclosures If you think you’ve found a potential security issue, please do not post it in the Issues. Instead, please follow the instructions [here](https://aws.amazon.com/security/vulnerability-reporting/) or [email AWS security directly](mailto:aws-security@amazon.com). ## License The Amazon ECR Docker Credential Helper is licensed under the Apache 2.0 License. THIRD-PARTY-LICENSES000066400000000000000000000443231443273566100137740ustar00rootroot00000000000000This Amazon ECR Docker Credential Helper includes the following third-party software/licensing: ** github.com/aws/aws-sdk-go/; version 1.21.2 -- https://github.com/aws/aws-sdk-go/ ** github.com/jmespath/go-jmespath; version c2b33e84 -- https://github.com/jmespath/go-jmespath Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. * For github.com/aws/aws-sdk-go/ see also this required NOTICE: AWS SDK for Go Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. Copyright 2014-2015 Stripe, Inc. * For github.com/golang/mock see also this required NOTICE: Copyright 2010 Google Inc. * For github.com/jmespath/go-jmespath see also this required NOTICE: Copyright 2015 James Saryerwinnie ----- ** golang.org/x/sys; version 1 -- https://github.com/golang/sys Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----- ** github.com/pmezard/go-difflib; version v1.0.0 -- https://github.com/pmezard/go-difflib Copyright (c) 2013, Patrick Mezard Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----- ** github.com/davecgh/go-spew; version v1.1.1 -- https://github.com/davecgh/go-spew Copyright (c) 2012-2016 Dave Collins Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ----- ** github.com/docker/docker-credential-helpers; version 0.6.3 -- https://github.com/docker/docker-credential-helpers Copyright (c) 2016 David Calavera ** github.com/konsorten/go-windows-terminal-sequences; version 1.0.2 -- https://github.com/konsorten/go-windows-terminal-sequences Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de) ** github.com/mitchellh/go-homedir; version 1.1.0 -- https://github.com/mitchellh/go-homedir Copyright (c) 2013 Mitchell Hashimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----- ** github.com/stretchr/testify; version v1.3.0 -- https://github.com/stretchr/testify Copyright (c) 2012 - 2018 Mat Ryer and Tyler Bunnell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----- ** github.com/Sirupsen/logrus; version v1.4.2 -- https://github.com/Sirupsen/logrus Copyright (c) 2014 Simon Eskildsen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. VERSION000066400000000000000000000000061443273566100121350ustar00rootroot000000000000000.7.1 docs/000077500000000000000000000000001443273566100120215ustar00rootroot00000000000000docs/docker-credential-ecr-login.1000066400000000000000000000075741443273566100173540ustar00rootroot00000000000000.\" Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. .\" .\" Licensed under the Apache License, Version 2.0 (the .\" "License"). You may not use this file except in compliance .\" with the License. A copy of the License is located at .\" .\" http://aws.amazon.com/apache2.0/ .\" .\" or in the "license" file accompanying this file. This file is .\" distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR .\" CONDITIONS OF ANY KIND, either express or implied. See the .\" License for the specific language governing permissions and .\" limitations under the License. .TH DOCKER-CREDENTIAL-ECR-LOGIN 1 2018-10-29 AMAZON-WEB-SERVICES AMAZON-ECR .SH NAME docker\-credential\-ecr\-login \- Amazon ECR Credential Helper for Docker .SH SYNOPSIS .B docker\-credential\-ecr\-login COMMAND .B docker\-credential\-ecr\-login -v .SH DESCRIPTION The Amazon ECR Credential Helper for Docker is a credential helper for the .BR docker (1) command that makes it easier to store and retrieve container images with Amazon Elastic Container Registry. .SH USAGE Place the .B docker\-credential\-ecr\-login binary on your .IR PATH and set the contents of your .IR ~/.docker/config.json to be .nf { "credsStore":"ecr-login" } .fi With Docker 1.13.0 or greater, you can configure Docker to use different credential helpers for different registries. To use this credential helper for a specific ECR registry, create a .IR credsHelper section with the URI of your ECR registry: .nf { "credHelpers": { "public.ecr.aws": "ecr-login", "aws_account_id.dkr.ecr.region.amazonaws.com": "ecr-login" } } .fi Once installed, you may use \fIdocker pull\fP and \fIdocker push\fP with ECR repositories, without running \fIdocker login\fP. .SH AWS CREDENTIALS The credential helper reads AWS credentials from standard locations, including environment variables, the shared credentials file (\fI~/.aws/credentials\fP), EC2 instance profiles, and ECS task roles. To use credentials associated with a different named profile in the shared credentials file, you may set the \fIAWS_PROFILE\fP environment variable. The credential helper reads and supports some configuration options specified in the shared configuration file (\fI~/.aws/config\fP). To disable these options, you must set the \fIAWS_SDK_LOAD_CONFIG\fP environment variable to \fIfalse\fP. The supported options include: .IP \[bu] 2 Assumed roles specified with \fIrole_arn\fP and \fIsource_profile\fP .IP \[bu] External credential processes specified with \fIcredential_process\fP .IP \[bu] Web Identities like IAM roles for Service Accounts in Kubernetes .RE The credentials must have a policy applied that allows access to Amazon ECR. See http://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html for more information. .SH OPTIONS .TP 16 .BR -v Print the version and git commit used to build docker\-credential\-ecr\-login .SH COMMANDS .TP 16 .BR get Retrieve credentials for a specific Amazon ECR registry. The registry should be passed to the credential helper via .BR stdin (3) and results will be printed to .BR stdout (3) .TP 16 .BR list Retrieve the names of each registry currently known to the helper. The helper will cache the names of all requested registries and return them here. .TP 16 .BR version Return the version of the credential helper protocol implemented by this helper .TP 16 .BR store This command is not implemented and is a no-op. .TP 16 .BR erase This command is not implemented and is a no-op. .SH BUGS Kubernetes users using the credential helper inside containers with a non-root user may encounter permission issues described here: https://github.com/kubernetes-sigs/external-dns/pull/1185. You may be able to work around this bug by adjusting the Kubernetes \fIsecurityContext\fP. .SH COPYRIGHT Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All rights reserved. .SH LICENSE Licensed under the Apache License, version 2.0. docs/ecr.png000066400000000000000000000211321443273566100132770ustar00rootroot00000000000000‰PNG  IHDRô·äm¬übKGD˙˙˙ ˝§“ pHYs.#.#xĄ?vtIMEâôŹI IDATxÚíÝy|Üôť˙ń—ś;ÜPŽRŽB"'@©Šs‘rÉ-š^đK»´}´ÜG¸iş-[ŘŇîBˇĐ–”BˇËQş”…żbq5$€…Ř ”†ű,ŘNFű‡4¶F#i4>Ć“řý|<ćáÍŚ4Ł‘őŃç{‚H}đ;Ú6đ;ÚÂűÚ!""«ˇí‚1Ŕ—ěü}nŽáwŘ—O ý޶Fk»‚şşÔw ß­xŻŃ_˛Ç?±ü÷€ŁŔß,ň;Ú~lÉ޵ăDDV†vÁ ćOěŠ1íQü%»í\FëŔĎo„wűż\b´¶źQ ęFk»v˘2tť ľKôŃ&ţ»^ ţC@k¸Śţż~ÉăM€źřmŻúm3€ńĘÖED”ˇËhóÇgbL_öY?Žc㔌ĽüŻQ˛ě/Ŕ‘Fkű Úł"" čR“@> cúřŹĎě†ÁÁŘ`ŕçŽo#+¨—-?řąŃÚţľö´H}Q‘űšĚźóé;Cá˙ߎďo€ď§ű@ˇôŻ_<»ůám`Ůgü޶Sű·©bxt† ľxç«üuýǧ^ ~ř{–Ő‘ű‘ÇŃżĺ»<°Ľg+ŕ,żŁ­Óďh۱ş+°‹Ś.ąŻÖÁÜÂáűé`lV±ŽĽ¤=WQ{Öówß7ZŰ»Ô^DDşTČíÜ)üĹÖNţâĎýüźżYţŚśĽyůß’ń|čô;Ú~Lč˙ŚĘŘED”ˇK® ľçűW—‘'dćŐeäĄë(}®ř…ŃÚľ@żşÄ÷cŰcěň|xÇu0ŚÓ&hc0™AĂg^4tűžŃÚî«(^DD]Á|áöłŠÁ|‡€ů`l=Ľyüýą3ň„ŔßżÜ Ě7ZŰ—)¨‹( Źí€ţ×í vîĹ`ËŚ:„Ś3ňŘűJ–-ćo§jUQĽúšĚśBĂžK‹ýP NĂ`»’©?&ĺż\IpŻzśöa¸(H{ćş§­íoéQ@_˝yűdÚ:)´›Đ0îS@;-•caĄ ž#ë®IĐĎőŢĂkŚÖöU:"DDOu裹óŰ:)8“·„†«đýżăű-¤Śéâ—TMëŞ)żeÖeSľÂÔÁe˛ęć©#»/ËŘł2rż´Ž>oáA‘?‹aěkL{ě˙‰YÓü¦‰4ţs7}7MlÂŕ`Ŕ>ô耩LučŁĚsÖ™—TW§ľ':.{ňúü’ ?y›ńJů¬•eö_/űŔÝŕcL_Ľ“1mŃ+ţ3¸?m!}7N,^lĐwÓÄ3€ř´ă€F,"Ő3-{Pω2t©: '—gĺŘ>řaVśÚ%!«h=1źŹe÷Ńşu„Ś<ˇ\ÝŻ”‘§q¶1ýńţÖnĆ´EôÝňĆşé»qâA¸ X;ZmPë ç:U?'2ÚÇgŇk<×Á´ě`'`Bäżj™ç:ݤ­WÇşş 6¨GŁaJ×đx°®Ô†-ÚČ.m}eý׍„@]R ý¨Yý×K–ßĚ7¦wüŻ˙řÔţ—öý~Ťß~ĆouÓwĂÄÝűnřS`ŻňuŐVx|ŘŠ 9~Ń`đŽZ©2o<ÍĐJA׾čąÎc9^»ąiŮ_ö'¨˛úDF†ţĚ /·^5-{wĎuŃĎ®€>6zV@Í“š¦ç ”Ť—–­'ö¬ţëAťű™ĐđḈWř‹>‹1óéţuŻőťč˝~Ňŕj|6-M¶żÄßH*ľ¨Uvľ6Đ–ń’F`oÓ˛ďUĆ!ô´ç:7á-«ë6¶|X,÷\'錰8˛ŽĆç˙ßy®SČńůĆű˙.şĹ´l xK˙3µ/’ŃęŐŽô–řw …{îńiŮoYăö´ŃéúÜţNĆĚgOŠţK†H0ď˝nŇôŢë&ý¸Ř4iŰ~ÉDpaQľ_Ó }k`ÇزU±˙‘/ëÄ$C0¤KÔř±>ľ+Ěď6đ\ç/ŔÇ)Á<ľŽ>Ďu–Óç^éąÎťŔ7ÂŚ~ ŕ”°:@żľúć)=ľü´îhń[tTŐ<=áµĺë‰MÓZđ“>Ç«řLĂ(|ŃŘĺągýǶÇůlÉWěýݤ†Ţë&]< ěSścŰO ě#—ŞGN4§Ćžú€ 4ęŔŘ{DFŤiŮçłÂ‡‹€Ży®łj´ę­#űvŕďŃ˙+]+ Żá˝BîÇ‚[…qÜÓ2p?+»ĎYŕ—~îןłž˙”1ëů%Ś€±ËóIßr=|'ÚH')ëN ěµ; Ť'hŃuIýDmaZöyNNiAż¸<ţĽiŮë–˝žiŮFÚ:7†ďY{1Ńe†iŮë†ë[o°ë©Ak®ć3eż–=.Ü'ëŽö÷Žoß´ěM#Çěr`·b±řhOĎuŰ™ÜůĽ3uÂŻ-ŐˇŹF†^r'ŢĽ<9şŁ%˝Óʬ°b‹ř´úő`{gźţíţőÍzľňĹbŇŞÓ–±mÖĆŃ ËÎ /tŻ-?źô–đń …Ż}á˘qŔ=žëôDžźĚve ˇTÁ´ě%Ŕ<Ďu^ŠŹđ='†Ą›„ëőMË~řŤç:ç¤ehĹĺáz&„ëŘ/ü>ë3ĐŰ0-{%AC¬›=׹<úţŘçů<Őµ†Ž+y®óʤĎŢßř°Đ\L@LËîž®đ\綤Ď[炆ckE¶ý đaäű¬Ěľ l^Ü'áţ¸ďąÎ{ŁŮz;üś6Ĺę+8Ćsť•uÖ˘ü©Čý–°AĐ×ŕ€ž§ďx¬WXęĽç•Zćë-›–<©[\y ÷‡08®a·Ąî .ZŚüAĽÖÝŐL˶¨=×ů(|ľťŇĆrSMËŢx9ÇÉóöŘă}MËţ 08— o{’ýMËţpNxźfZö© źµh3ŕlÓ˛'č řrĘ÷=8Ř=ÇîŮh3-ű2ŕ[žëÜ4.f ńgřÁż%ÁÜ´ěYáÔ.ďÝŘÇ´ěwĂ‹ ›ŠA żĎf ¶˘Ýşöö\ç/¦eďhZö÷C3¶upiŮz®sűheçá÷:)\ô.đ@v‹źÔ ż¶Tä>ZA=ď©ĹbřBBĂąĚâw’ë×3ęđ#čď…'ţ˝v_ęž2ÄŇ„ď˙®iݎ“#°0=öÔÍ‘űóŢ:g›<†`‘Ű2‚yÔŮŔ¦eß ü5#—|-`±i٦\Ţ]‘3ÇÝ`Zö÷8-âĎĐCdźH0?• őö.9׳pŁiŮwK!RJ>Š-k3-ű×ݻ͹­MËž5Eď‘ďµ[ř÷uĎu^®Ă:ę­#÷_ÓÉ^}l󤀚ô\¸¬¬áë‹_0D{ĘEŔ{Ŕą vçF {,}?(6nŘ}éđ|ß´@ź'ř¦3X7`ë) 3Çe±·m-wż L‰•?|Lv?ŕ[})Óľ7|OOF¶ţ­ś—WĹĎđ&đ7‚ŮíŇ\cZöş±ŕ1Ą{F, ś•r^ęŢ˸Řß´ěk#ż_%§‡ŮîZ±ýűNĆţ] 8n´‚¨iŮŃ ˛zíç=;r±NřµĄ"÷ZÇs?v6Ë:÷f áJ¬ßxƨpĄĹŰáÄ/ĺőëW§5´uľ Pp&Ó°ç ąůŔiĹîyë×GÎٱÇox®-ţ] <|;ňš/–=Ţsť•Üć;aŔúSLW ďyŮ}áo®Ľ0č¬űË^»7piÂňbş?†·…aÖÚ˵†ş=řnÂűŹ΋<ľ¸/ÇeWřb$ł,ţŇŤŔ[‘ý˝pqĘ:ć÷„źu<Á`&×Q^‡˙]ŕJĎu®ňwů#ABwd˙î üfKi†ĂΑű ëéĽ^D­ĎŔ 7oz®Ó©3ľúšŃŁőÚI7a¸×ţ?ŃMŁĎVš'˝l>†ŹĎłľÁŃăfw>Rx`2…&Ó0»“{¨˙‹‘Áb*ŐťY Ă+Ň(m‹ŘSĆ^ç›–}g, CĐçß«¬»ü Óu±`Ń˙#_{“ŘűľäąÎ{±LĎu„ ĺ~?·¦\Ňí﹎“RZ}ü˝°Á[Klv1 ‡Ż»&ç‰ţÓŔ ÇľžëĽůNëÄ^÷6đĎu^Ź}Ć—MLË~Ś`”˛xÉFŢşŰ˙-®?¶Ż6-űmŕŽŘ{6Äa·ˇiŮ[‘hÖŕEĎuúbË'FîżPO§µpż|*\t’Nöµ§"÷štĘëµ yë¶É.†Ďzm!q˝wáűSÇíŐůňŮťĂóŁçnßČ®;Ż}]úq Ë~ť°ě¶„e?*žŔް ¸-Z\¬ď ooLrwo±eut›‘m˙&Ď˙t¸ '~!‘2` Ŕ‚„őnPěZ—÷»‡O%\¨âąÎ˝‘ş!ÁXŕq§z®ózB‹˙ţőP>‰Čć¦eďź3})şţŘle„%4Ż%|ŻÍŞ<Ţ!h•ż$çíąH`ŚŠ^¨ĽQyôńeŔŻÂ‡Ŕ­·AúŘ ęŃ;i-Ü!˝%zĘLjFÎě>ü{Ǹ/tőŤŘw4âÝŻśŤŹ`vž`ÖŁ´ř‚Y˘VĹşLv úĄEĐk›–˝W8*W5Ĺă*ÂW€Ď&çŚ÷­¬vŹĺ Ć]CŮÇ‘îq&d´żń\çšXéŔVŐ\›uááąÎ˙–íR޸ńŔ0űô> żĂJ‚jO&?ĺ¶ŐŤăn¤$[ŃŚZĚ-0Ó´ě´ˇ_}Ó˛· ˙—g Jç9 Ísť^DúŘČĐ“3p?Ţť*˛ő¤‘Ţ RĂąń5ű®%Ůş‘ü:22öaž¸·ŁĽĄůőń{äţ «úĘd+ky¦ 23śç…?ĚÇć‡'Ń© ďĐsťŢűůš„eSÂA†ŞŔŔxCq5AŁČĎĺĽíĚŔkie_µ­áQ‚şú¤Űcm;Nó“©žë| ě\úŘ îy¦HMěožł˙zÉ˙~ŽţëĂY‘YwŢ?ąKöű†1€…AäG±§>&Ą5nxRz9Ě:v<58iuš"2Vú°)đ‰°čxĂpĎżĽöëž0ÄěüŇ„¬ű9ŕř„z{`ϸ‡r–(´',Ű ľzeťěţ7=×é†őDűxoFöLlĂá…Ś„íÂżÇz®ó»čEˇ†|U@»A=#"Vś"ŐŻ0Ň[ÉłµiFŢż•ŠÇ‘i×F6°GŠŠ=µ4ídž”V™–ýÇX@ź´z®Ó±:sÓ˛7&(ĆžJPŚÜ‘ľ;¬4˛ŤCúÝG˝ĚL™ Ä ¨É ^™Á2aY3Cź‡Ľ˝ążE ¶·3ÉEű+ zĹLĐősyü˘QĐŘËę—¦HMĘ®ÓGzhź4˙z-®UüH)CĄŔnř9.v†$i¨×VÓ˛łĄł€ýęý$ ´_˘|Ň™¸1śkdmĹËQ˙łő3V‘t0ňlŰsť7ĆPńîÓ±`űź#Ľ˝ĺářěI1-{‚Ć{ç™–}żç:ď"ŁJučőÔ+ͤ–ŘZťě WęŘK+ÚGč{Ä{Ĺf|cÄŞÂůźż1Ś«Ü×´ěMV“`>5#żF0HÉ#ýÜ«şśŠlcAî¸/{®ł´B07rů¤ío<†ÎŹFîĎÍă*tpÁoC0TŻf$T@—ôô69XűUĚ“žkF·‘ľ@!6ťzFđOěć6<6'd8XχPäbăľ„§ź>íąÎžëěŢZ<×1Z-y·aZö:Ť¤ÖŹ=}°ç:N…RŚÉŁÔµäüšŰ%e–Ô¦x­/Î^Ť”tlcZöĆŁ9 ­ç:÷×´ě9*rW@—<™n,Xűicł§d÷‰ý×ýüÜ)ŹËç>Ż؇ϗ(Ż«}ť ~7ď-nŽiŮuý?dZöl )‹ÝËsť—SÂ[yŚiŮÍ ŘâEőç{®smĄ* Ďu|’ëË÷Ë™ńÍNůü+Ö¤SAd^ţÝřlĐŻGŽ—‹Ăé€uî%ŞC_ť˛őH¬+qŽ ç’úŻ5řĚ)C˝–T™×¦úżĹĚň\çĹ*‚ăß(m‘ý‚ÖÔËëřčŮ!aŮSžëĽ–l«ÉÎ/ZcOÝáąÎÉU´/x+MË>‚`xU€ÓMËv=׹-VrQËĎÁdAË ş ^`Zv{qQ@—\§Â´…úݧŻ`Čz®hŁĐ‹o”MçV˛IŁüqj1ü˛‚AFf&<}Ń V™ÔR|SÓ˛gy®ł°NŹ”'–cËĎ-ŃiZv Á÷ß$h@uaóIŕ†„§—·“ŻäŻ@P<[,n˙÷°´c›ŘůéyÓ˛o łř—T™~Îřę«*®©ŤłÂ€ýdŘß˙·áoy«iŮż%४>áĂq®Ł/ĽČűĎđ7<8V}ÓĐe0=ˇÎĽ¤˙: Át˛áž-ĐMGt4Ph_˛Ń*2ö’l}˛‡0Pm{ęaĎuţQÍÉ&|íňp¶/ÇžžKťMgŮ‹LËhú+@ŹiŮďSý bĄ,߉ňá^Ó¬Š^Ty®łÂ´ěă †ŚŤ^Śţ9ĽUrç:ĎÔáĎ0!ě Đ\ĺű ŕýč¬k‘Ůŕ®1-{ǰôÉ ćÓ˛/!†ö5Ó˛ű §B¸ÇL[»°x°Ő0fé·OÓ€y¦e˙Ásťűu’®ŐˇŻI=ĄÎ<±5|±Eü0h:Ş‹¦#şčą˘ĺ« ś~V6f{ž–íIőëCËÎ!ą¸ýújł¸ČkOKxzÓ˛Ç×[ëŢČçŮH+ţL ć' SëĚ󿀯2¸ę?ö\çűuúßyB¸?ßÄmVÚ±6>Űź`bź˘c ş$ľL0ÓÝb‚1:Â密Ą6g[óĹŁ~ž˘«MË^_­ŢĐe¸;¤÷_JV~yđőśßBĎ‚–Mz´Ü‡ĎÄ[` yüűę,ţ~oߏݭ€ů*rŻąŻÉ=ˇáśOyŐvŐÁü˛šŽî˘çň–ńŔyř†Áş%§#!¨Ç‹áË^Gň±·.A·žU±Ös9ÉDćÉŢ—`R“č…qôÓîAéÄ +¨ÜµíTŕÜČz¨<žů?€¶Ř˛ŹSN´Ď™–˝%ÁÄ2{ ČŇů…ŔBĎuEŢv8Ą}Ë˙ůl/%ě×ÁˇK“öo8€ĘtÓ˛?KĐţag`[‚^/ĎK<×y8ňÓ¶ő:A5Éř*Ď}˝Ŕa”N[:ž`ĽűJŢ'¨ëJĹVĄĂ˝¦–Âx®s1A?đĎ„żíÖa>!rÜżB0‹ŰßÂ˙öŰ9˝Ş.Ă‹üÜJ0ď8%ŤµehÔVß-Ź.•_9úkmÖŽn<ŕ…čă€9±PyđŚW'żď`zÓa^ŹŽš‘WŹŤ—*}&5¸Ş~żhź­Ůtő4V˛ő¤břśCĹ­¸t =ŐŠËZţɇ;đi¨XTž¸,6|wŢ÷ɩǓ|ĄĎ¤ŔTý~Ń>[ł©Č}¬ö*Z¸Ż¸¤…ćc»hž×ĹŠK[šßâ­Ť}#˛ŠŚ>ćd˝&Ţż6ł»Š( ËÔ©ÔW\<™ćc;YqÉÄFw:>§`DęýHŐxR0N«#/[–Rż® ."Rą+¸—ň‹&G2ôÉ6Ś8X'­XÜOX–ú8uYŽÖđ""˘€^G>®×¶â˘É4ßÉŠ‹'ď üźv|Ö)™˛:%@ÇgkÍÄăÁß Ň]!]DD˝>őÝ2ŕ.‚ţľu§ůřNV\4ů|ö)Ľ9˛Ĺş_ąT y™qYÓ^ŻŽôşÓ8·›ĆąÝo7ÎížĚ^«« ýÂÉźŔç%Á¸¬řĽ­™™¸źUěî“U45°mÓ‘]—ő,¬FDD˝®łtçv_Ö8·{ ŕ‚:úx°˛bVíG_NjáxĹbřŇe]ŔÖMGvÖtd×K=W´ĐtT§ôúÍŇ‹˝ď–‰4Îí>‰`$¬?ÔŐÍÔű3vŇ{ĺbřń9Ľé¨®ÉMGu˝Ňł čóŢtd—‘*¨ŰÚ(öđţSŔ}·LśÜGőł_ _Ďęfźţ´˙uFißµ”÷%ts;łé®ţÉNz´Đt”ą2ôŐX­/nśŰ˝!đC‚ń§G'¨çmˇ^ö8a¸„őřpłSšŽé:­çŇ–ţ‰^ĚEDĐ׌˝Xżü‚`‚…«Gí fV´’břÄúő7€ťń9¨ů®N€¦y]4­@."˘€ľ†őâßĆąÝËçvěÜSł >e©ŻéĎÖ—ásbóĽ®Í›çu=…ú—‹( Ź‘Fs‹€ýÂŔţîo8«KYžěĽl]Ć/m›Źíş‚‰^šç)#Q@CŮz¬áÜ"`Sଠě®;O|Ď;7×9ßč+>­`."˘€>¦{­ŻjśŰýc`ŕ¦IΓfUÍ[—,{ hĹgNóqťO­¸x2ÍÇ©?ąşôučo ˙aăÜî€=G†o+-ÔýęÇb8¶h>ľóÉâ3 ć"" č’ŘĂűłý°¤č íęG&]©ŘŻ&7źĐůÓć«Ţ|ĽąH-i`™Ő?Ŕ÷wăún™řkŕp`ݡőb˛nPě8îF|Đv`~ó‰ťOč—Q†OCĽćŰIDAT.Céż~2°5Ă1ڬOŮ`1‘lý#  ź}šOě|bĹšDEDD]†#KŹţ}Żqn÷ŔŕÉÁń„Ś=ěďăsvóIťë6źÔé}Í'Şx]DD]†=¸‡ çîfď {đř·Ŕ¶Nî<`ůŻ'Ó|’ąşŚxƬlśŰý`CŕZŕĂŞ;,öšpĘŇCńyoůݦ0áds‘z˘Fqc#[˙8¸ď–‰?®ÚÂ—Ś‹pŁ˙"Ď 8÷Nż´`Â)KµSED”ˇË(gë4Îí~©qn÷l‚břű)#ţmŕF`g„ď/˝ ŁĘâzy‘ń‰ŹŁ–ź7E;LDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD¤ćţЕǡY>IEND®B`‚docs/packaging.md000066400000000000000000000025771443273566100143020ustar00rootroot00000000000000# Packaging Amazon ECR Credential Helper Amazon maintains packages for the Amazon ECR Credential Helper on the following operating systems: * Amazon Linux 2 ([source](https://github.com/awslabs/amazon-ecr-credential-helper/tree/amazonlinux), [packaging documentation](https://github.com/awslabs/amazon-ecr-credential-helper/blob/amazonlinux/docs/packaging-amazon-linux.md)) * Debian ([source](https://github.com/awslabs/amazon-ecr-credential-helper/tree/debian)) (note: the packages in derivatives of Debian like Devuan, Ubuntu 19.04 Disco Dingo, and PureOS are derived from the Debian package with no additional modifications) There are community-maintained packages for the Amazon ECR Credential Helper on the following operating systems: * Mac OS X (with the Homebrew package manager) ([source](https://github.com/Homebrew/homebrew-core/blob/master/Formula/docker-credential-helper-ecr.rb)) * NixOS (and the Nix package manager) ([source](https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/admin/amazon-ecr-credential-helper/default.nix)) * Arch Linux (in the Arch User Repository) ([source](https://aur.archlinux.org/packages/amazon-ecr-credential-helper)) If you are interested in packaging the Amazon ECR Credential Helper, please get in touch! We can list your community-maintained packaging here and include installation instructions in our [README.md](../README.md). ecr-login/000077500000000000000000000000001443273566100127505ustar00rootroot00000000000000ecr-login/api/000077500000000000000000000000001443273566100135215ustar00rootroot00000000000000ecr-login/api/client.go000066400000000000000000000237021443273566100153320ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package api import ( "context" "encoding/base64" "fmt" "net/url" "regexp" "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecr" "github.com/aws/aws-sdk-go-v2/service/ecrpublic" "github.com/sirupsen/logrus" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache" ) const ( proxyEndpointScheme = "https://" programName = "docker-credential-ecr-login" ecrPublicName = "public.ecr.aws" ecrPublicEndpoint = proxyEndpointScheme + ecrPublicName ) var ecrPattern = regexp.MustCompile(`(^[a-zA-Z0-9][a-zA-Z0-9-_]*)\.dkr\.ecr(-fips)?\.([a-zA-Z0-9][a-zA-Z0-9-_]*)\.amazonaws\.com(\.cn)?$`) type Service string const ( ServiceECR Service = "ecr" ServiceECRPublic Service = "ecr-public" ) // Registry in ECR type Registry struct { Service Service ID string FIPS bool Region string } // ExtractRegistry returns the ECR registry behind a given service endpoint func ExtractRegistry(input string) (*Registry, error) { if strings.HasPrefix(input, proxyEndpointScheme) { input = strings.TrimPrefix(input, proxyEndpointScheme) } serverURL, err := url.Parse(proxyEndpointScheme + input) if err != nil { return nil, err } if serverURL.Hostname() == ecrPublicName { return &Registry{ Service: ServiceECRPublic, }, nil } matches := ecrPattern.FindStringSubmatch(serverURL.Hostname()) if len(matches) == 0 { return nil, fmt.Errorf(programName + " can only be used with Amazon Elastic Container Registry.") } else if len(matches) < 3 { return nil, fmt.Errorf("%q is not a valid repository URI for Amazon Elastic Container Registry.", input) } return &Registry{ Service: ServiceECR, ID: matches[1], FIPS: matches[2] == "-fips", Region: matches[3], }, nil } // Client used for calling ECR service type Client interface { GetCredentials(serverURL string) (*Auth, error) GetCredentialsByRegistryID(registryID string) (*Auth, error) ListCredentials() ([]*Auth, error) } // Auth credentials returned by ECR service to allow docker login type Auth struct { ProxyEndpoint string Username string Password string } type defaultClient struct { ecrClient ECRAPI ecrPublicClient ECRPublicAPI credentialCache cache.CredentialsCache } type ECRAPI interface { GetAuthorizationToken(context.Context, *ecr.GetAuthorizationTokenInput, ...func(*ecr.Options)) (*ecr.GetAuthorizationTokenOutput, error) } type ECRPublicAPI interface { GetAuthorizationToken(context.Context, *ecrpublic.GetAuthorizationTokenInput, ...func(*ecrpublic.Options)) (*ecrpublic.GetAuthorizationTokenOutput, error) } // GetCredentials returns username, password, and proxyEndpoint func (c *defaultClient) GetCredentials(serverURL string) (*Auth, error) { registry, err := ExtractRegistry(serverURL) if err != nil { return nil, err } logrus. WithField("service", registry.Service). WithField("registry", registry.ID). WithField("region", registry.Region). WithField("serverURL", serverURL). Debug("Retrieving credentials") switch registry.Service { case ServiceECR: return c.GetCredentialsByRegistryID(registry.ID) case ServiceECRPublic: return c.GetPublicCredentials() } return nil, fmt.Errorf("unknown service %q", registry.Service) } // GetCredentialsByRegistryID returns username, password, and proxyEndpoint func (c *defaultClient) GetCredentialsByRegistryID(registryID string) (*Auth, error) { cachedEntry := c.credentialCache.Get(registryID) if cachedEntry != nil { if cachedEntry.IsValid(time.Now()) { logrus.WithField("registry", registryID).Debug("Using cached token") return extractToken(cachedEntry.AuthorizationToken, cachedEntry.ProxyEndpoint) } logrus. WithField("requestedAt", cachedEntry.RequestedAt). WithField("expiresAt", cachedEntry.ExpiresAt). Debug("Cached token is no longer valid") } auth, err := c.getAuthorizationToken(registryID) // if we have a cached token, fall back to avoid failing the request. This may result an expired token // being returned, but if there is a 500 or timeout from the service side, we'd like to attempt to re-use an // old token. We invalidate tokens prior to their expiration date to help mitigate this scenario. if err != nil && cachedEntry != nil { logrus.WithError(err).Info("Got error fetching authorization token. Falling back to cached token.") return extractToken(cachedEntry.AuthorizationToken, cachedEntry.ProxyEndpoint) } return auth, err } func (c *defaultClient) GetPublicCredentials() (*Auth, error) { cachedEntry := c.credentialCache.GetPublic() if cachedEntry != nil { if cachedEntry.IsValid(time.Now()) { logrus.WithField("registry", ecrPublicName).Debug("Using cached token") return extractToken(cachedEntry.AuthorizationToken, cachedEntry.ProxyEndpoint) } logrus. WithField("requestedAt", cachedEntry.RequestedAt). WithField("expiresAt", cachedEntry.ExpiresAt). Debug("Cached token is no longer valid") } auth, err := c.getPublicAuthorizationToken() // if we have a cached token, fall back to avoid failing the request. This may result an expired token // being returned, but if there is a 500 or timeout from the service side, we'd like to attempt to re-use an // old token. We invalidate tokens prior to their expiration date to help mitigate this scenario. if err != nil && cachedEntry != nil { logrus.WithError(err).Info("Got error fetching authorization token. Falling back to cached token.") return extractToken(cachedEntry.AuthorizationToken, cachedEntry.ProxyEndpoint) } return auth, err } func (c *defaultClient) ListCredentials() ([]*Auth, error) { // prime the cache with default authorization tokens _, err := c.GetCredentialsByRegistryID("") if err != nil { logrus.WithError(err).Debug("couldn't get authorization token for default registry") } _, err = c.GetPublicCredentials() if err != nil { logrus.WithError(err).Debug("couldn't get authorization token for public registry") } auths := make([]*Auth, 0) for _, authEntry := range c.credentialCache.List() { auth, err := extractToken(authEntry.AuthorizationToken, authEntry.ProxyEndpoint) if err != nil { logrus.WithError(err).Debug("Could not extract token") } else { auths = append(auths, auth) } } return auths, nil } func (c *defaultClient) getAuthorizationToken(registryID string) (*Auth, error) { var input *ecr.GetAuthorizationTokenInput if registryID == "" { logrus.Debug("Calling ECR.GetAuthorizationToken for default registry") input = &ecr.GetAuthorizationTokenInput{} } else { logrus.WithField("registry", registryID).Debug("Calling ECR.GetAuthorizationToken") input = &ecr.GetAuthorizationTokenInput{ RegistryIds: []string{registryID}, } } output, err := c.ecrClient.GetAuthorizationToken(context.TODO(), input) if err != nil || output == nil { if err == nil { if registryID == "" { err = fmt.Errorf("missing AuthorizationData in ECR response for default registry") } else { err = fmt.Errorf("missing AuthorizationData in ECR response for %s", registryID) } } return nil, fmt.Errorf("ecr: Failed to get authorization token: %w", err) } for _, authData := range output.AuthorizationData { if authData.ProxyEndpoint != nil && authData.AuthorizationToken != nil { authEntry := cache.AuthEntry{ AuthorizationToken: aws.ToString(authData.AuthorizationToken), RequestedAt: time.Now(), ExpiresAt: aws.ToTime(authData.ExpiresAt), ProxyEndpoint: aws.ToString(authData.ProxyEndpoint), Service: cache.ServiceECR, } registry, err := ExtractRegistry(authEntry.ProxyEndpoint) if err != nil { return nil, fmt.Errorf("Invalid ProxyEndpoint returned by ECR: %s", authEntry.ProxyEndpoint) } auth, err := extractToken(authEntry.AuthorizationToken, authEntry.ProxyEndpoint) if err != nil { return nil, err } c.credentialCache.Set(registry.ID, &authEntry) return auth, nil } } if registryID == "" { return nil, fmt.Errorf("No AuthorizationToken found for default registry") } return nil, fmt.Errorf("No AuthorizationToken found for %s", registryID) } func (c *defaultClient) getPublicAuthorizationToken() (*Auth, error) { var input *ecrpublic.GetAuthorizationTokenInput output, err := c.ecrPublicClient.GetAuthorizationToken(context.TODO(), input) if err != nil { return nil, fmt.Errorf("ecr: failed to get authorization token: %w", err) } if output == nil || output.AuthorizationData == nil { return nil, fmt.Errorf("ecr: missing AuthorizationData in ECR Public response") } authData := output.AuthorizationData token, err := extractToken(aws.ToString(authData.AuthorizationToken), ecrPublicEndpoint) if err != nil { return nil, err } authEntry := cache.AuthEntry{ AuthorizationToken: aws.ToString(authData.AuthorizationToken), RequestedAt: time.Now(), ExpiresAt: aws.ToTime(authData.ExpiresAt), ProxyEndpoint: ecrPublicEndpoint, Service: cache.ServiceECRPublic, } c.credentialCache.Set(ecrPublicName, &authEntry) return token, nil } func extractToken(token string, proxyEndpoint string) (*Auth, error) { decodedToken, err := base64.StdEncoding.DecodeString(token) if err != nil { return nil, fmt.Errorf("invalid token: %w", err) } parts := strings.SplitN(string(decodedToken), ":", 2) if len(parts) < 2 { return nil, fmt.Errorf("invalid token: expected two parts, got %d", len(parts)) } return &Auth{ Username: parts[0], Password: parts[1], ProxyEndpoint: proxyEndpoint, }, nil } ecr-login/api/client_test.go000066400000000000000000000535011443273566100163710ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package api import ( "encoding/base64" "errors" "testing" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecr" ecrtypes "github.com/aws/aws-sdk-go-v2/service/ecr/types" "github.com/aws/aws-sdk-go-v2/service/ecrpublic" ecrpublictypes "github.com/aws/aws-sdk-go-v2/service/ecrpublic/types" mock_api "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api/mocks" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache" mock_cache "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache/mocks" "github.com/stretchr/testify/assert" ) const ( registryID = "123456789012" proxyEndpoint = "123456789012.dkr.ecr.us-east-1.amazonaws.com" expectedUsername = "username" expectedPassword = "password" ) func TestExtractRegistry(t *testing.T) { testCases := []struct { serverURL string registry *Registry hasError bool }{{ serverURL: "https://123456789012.dkr.ecr.us-east-1.amazonaws.com/v2/blah/blah", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "us-east-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "123456789012.dkr.ecr.us-west-2.amazonaws.com", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "us-west-2", Service: ServiceECR, }, hasError: false, }, { serverURL: "210987654321.dkr.ecr.cn-north-1.amazonaws.com.cn/foo", registry: &Registry{ ID: "210987654321", FIPS: false, Region: "cn-north-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "123456789012.dkr.ecr-fips.us-gov-west-1.amazonaws.com", registry: &Registry{ ID: "123456789012", FIPS: true, Region: "us-gov-west-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "https://public.ecr.aws", registry: &Registry{ Service: ServiceECRPublic, }, }, { serverURL: "public.ecr.aws", registry: &Registry{ Service: ServiceECRPublic, }, }, { serverURL: "https://public.ecr.aws/amazonlinux", registry: &Registry{ Service: ServiceECRPublic, }, }, { serverURL: ".dkr.ecr.not-real.amazonaws.com", hasError: true, }, { serverURL: "not.ecr.io", hasError: true, }, { serverURL: "https://123456789012.dkr.ecr.us-west-2.amazonaws.com.fake.example.com/image:latest", hasError: true, }, { serverURL: "123456789012.dkr.ecr.us-west-2.amazonaws.com.fake.example.com", hasError: true, }, { serverURL: "123456789012.dkr.ecr-fips.us-gov-west-1.amazonaws.com.fake.example.com", hasError: true, }, { serverURL: "210987654321.dkr.ecr.cn-north-1.amazonaws.com.cn.fake.example.com.cn", hasError: true, }, { serverURL: "https://public.ecr.aws.fake.example.com", hasError: true, }, { serverURL: "public.ecr.aws.fake.example.com", hasError: true, }} for _, tc := range testCases { t.Run(tc.serverURL, func(t *testing.T) { registry, err := ExtractRegistry(tc.serverURL) if !tc.hasError { assert.NoError(t, err, "No error expected") assert.EqualValues(t, tc.registry, registry, "Registry should be equal") } else { assert.Error(t, err, "Expected error") } }) } } func TestGetAuthConfigSuccess(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String(authorizationToken), }}, }, nil } authEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.SetFn = func(_ string, actual *cache.AuthEntry) { compareAuthEntry(t, actual, authEntry) } auth, err := client.GetCredentials(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestGetAuthConfigNoMatchAuthorizationToken(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(proxyEndpointScheme + "notproxy"), AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword))), }}, }, nil } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } auth, err := client.GetCredentials(proxyEndpoint) assert.NotNil(t, err) assert.Nil(t, auth) } func TestGetAuthConfigGetCacheSuccess(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) authEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, ExpiresAt: expiresAt, RequestedAt: time.Now(), AuthorizationToken: authorizationToken, } credentialCache.GetFn = func(r string) *cache.AuthEntry { assert.Equal(t, registryID, r, "get from cache") return authEntry } auth, err := client.GetCredentials(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestGetAuthConfigSuccessInvalidCacheHit(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String(authorizationToken), }}, }, nil } expiredAuthEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now().Add(-12 * time.Hour), ExpiresAt: time.Now().Add(-6 * time.Hour), AuthorizationToken: authorizationToken, } authEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, } credentialCache.GetFn = func(r string) *cache.AuthEntry { assert.Equal(t, registryID, r, "get from cache") return expiredAuthEntry } credentialCache.SetFn = func(_ string, actual *cache.AuthEntry) { compareAuthEntry(t, actual, authEntry) } auth, err := client.GetCredentials(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestGetAuthConfigBadBase64(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(proxyEndpointScheme + proxyEndpoint), AuthorizationToken: aws.String(expectedUsername + ":" + expectedPassword), }}, }, nil } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } auth, err := client.GetCredentials(proxyEndpoint) assert.NotNil(t, err) t.Log(err) assert.Nil(t, auth) } func TestGetAuthConfigMissingResponse(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return nil, nil } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } auth, err := client.GetCredentials(proxyEndpoint) assert.NotNil(t, err) t.Log(err) assert.Nil(t, auth) } func TestGetAuthConfigECRError(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return nil, errors.New("test error") } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } auth, err := client.GetCredentials(proxyEndpoint) assert.NotNil(t, err) t.Log(err) assert.Nil(t, auth) } func TestGetAuthConfigSuccessInvalidCacheHitFallback(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return nil, errors.New("service error") } expiredAuthEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now().Add(-12 * time.Hour), ExpiresAt: time.Now().Add(-6 * time.Hour), AuthorizationToken: authorizationToken, } credentialCache.GetFn = func(r string) *cache.AuthEntry { assert.Equal(t, registryID, r, "get from cache") return expiredAuthEntry } auth, err := client.GetCredentials(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestListCredentialsSuccess(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} ecrPublicClient := &mock_api.MockECRPublicAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, ecrClient: ecrClient, ecrPublicClient: ecrPublicClient, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) authEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, } authEntries := []*cache.AuthEntry{authEntry} ecrClient.GetAuthorizationTokenFn = func(_ *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { return nil, errors.New("test error") } ecrPublicClient.GetAuthorizationTokenFn = func(_ *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return nil, errors.New("test error") } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.GetPublicFn = func() *cache.AuthEntry { return nil } credentialCache.ListFn = func() []*cache.AuthEntry { return authEntries } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Len(t, auths, 1) auth := auths[0] assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestListCredentialsCached(t *testing.T) { credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) authEntry1 := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, Service: cache.ServiceECR, } authEntry2 := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, Service: cache.ServiceECRPublic, } authEntries := []*cache.AuthEntry{authEntry1, authEntry2} credentialCache.GetFn = func(_ string) *cache.AuthEntry { return authEntry1 } credentialCache.GetPublicFn = func() *cache.AuthEntry { return authEntry2 } credentialCache.ListFn = func() []*cache.AuthEntry { return authEntries } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Len(t, auths, 2) assert.Equal(t, auths[0].Username, expectedUsername) assert.Equal(t, auths[0].Password, expectedPassword) assert.Equal(t, auths[0].ProxyEndpoint, testProxyEndpoint) assert.Equal(t, auths[1].Username, expectedUsername) assert.Equal(t, auths[1].Password, expectedPassword) assert.Equal(t, auths[1].ProxyEndpoint, testProxyEndpoint) } func TestListCredentialsEmpty(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} ecrPublicClient := &mock_api.MockECRPublicAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, ecrClient: ecrClient, ecrPublicClient: ecrPublicClient, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) authEntry1 := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, Service: cache.ServiceECR, } authEntry2 := &cache.AuthEntry{ ProxyEndpoint: ecrPublicEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, Service: cache.ServiceECRPublic, } authEntries := []*cache.AuthEntry{authEntry1, authEntry2} ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 0, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String(authorizationToken), }}, }, nil } ecrPublicClient.GetAuthorizationTokenFn = func(*ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return &ecrpublic.GetAuthorizationTokenOutput{ AuthorizationData: &ecrpublictypes.AuthorizationData{ ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String(authorizationToken), }, }, nil } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.GetPublicFn = func() *cache.AuthEntry { return nil } credentialCache.ListFn = func() []*cache.AuthEntry { return authEntries } setCallCount := 0 credentialCache.SetFn = func(_ string, _ *cache.AuthEntry) { setCallCount++ } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Len(t, auths, 2) assert.Equal(t, 2, setCallCount) assert.Equal(t, auths[0].Username, expectedUsername) assert.Equal(t, auths[0].Password, expectedPassword) assert.Equal(t, auths[0].ProxyEndpoint, testProxyEndpoint) assert.Equal(t, auths[1].Username, expectedUsername) assert.Equal(t, auths[1].Password, expectedPassword) assert.Equal(t, auths[1].ProxyEndpoint, ecrPublicEndpoint) } func TestListCredentialsBadBase64AuthToken(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} ecrPublicClient := &mock_api.MockECRPublicAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, ecrClient: ecrClient, ecrPublicClient: ecrPublicClient, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint expiresAt := time.Now().Add(12 * time.Hour) emptyCache := []*cache.AuthEntry{} credentialCache.ListFn = func() []*cache.AuthEntry { return emptyCache } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.GetPublicFn = func() *cache.AuthEntry { return nil } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 0, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String("invalid:token"), }}, }, nil } ecrPublicClient.GetAuthorizationTokenFn = func(_ *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return nil, errors.New("test error") } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Empty(t, auths) } func TestListCredentialsInvalidAuthToken(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} ecrPublicClient := &mock_api.MockECRPublicAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, ecrClient: ecrClient, ecrPublicClient: ecrPublicClient, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint expiresAt := time.Now().Add(12 * time.Hour) emptyCache := []*cache.AuthEntry{} credentialCache.ListFn = func() []*cache.AuthEntry { return emptyCache } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.GetPublicFn = func() *cache.AuthEntry { return nil } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 0, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String("invalidtoken"), }}, }, nil } ecrPublicClient.GetAuthorizationTokenFn = func(_ *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return nil, errors.New("test error") } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Empty(t, auths) } func compareAuthEntry(t *testing.T, actual *cache.AuthEntry, expected *cache.AuthEntry) { assert.NotNil(t, actual) assert.Equal(t, expected.AuthorizationToken, actual.AuthorizationToken) assert.Equal(t, expected.ProxyEndpoint, actual.ProxyEndpoint) assert.Equal(t, expected.ExpiresAt, actual.ExpiresAt) assert.WithinDuration(t, expected.RequestedAt, actual.RequestedAt, 5*time.Second) } ecr-login/api/factory.go000066400000000000000000000071471443273566100155300ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package api import ( "context" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ecr" "github.com/aws/aws-sdk-go-v2/service/ecrpublic" "github.com/aws/smithy-go/middleware" "github.com/aws/smithy-go/transport/http" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/version" ) // Options makes the constructors more configurable type Options struct { Config aws.Config CacheDir string } // ClientFactory is a factory for creating clients to interact with ECR type ClientFactory interface { NewClient(awsConfig aws.Config) Client NewClientWithOptions(opts Options) Client NewClientFromRegion(region string) Client NewClientWithFipsEndpoint(region string) (Client, error) NewClientWithDefaults() Client } // DefaultClientFactory is a default implementation of the ClientFactory type DefaultClientFactory struct{} var userAgentLoadOption = config.WithAPIOptions([]func(*middleware.Stack) error{ http.AddHeaderValue("User-Agent", "amazon-ecr-credential-helper/"+version.Version), }) // NewClientWithDefaults creates the client and defaults region func (defaultClientFactory DefaultClientFactory) NewClientWithDefaults() Client { awsConfig, err := config.LoadDefaultConfig(context.TODO(), userAgentLoadOption) if err != nil { panic(err) } return defaultClientFactory.NewClientWithOptions(Options{Config: awsConfig}) } // NewClientWithFipsEndpoint overrides the default ECR service endpoint in a given region to use the FIPS endpoint func (defaultClientFactory DefaultClientFactory) NewClientWithFipsEndpoint(region string) (Client, error) { awsConfig, err := config.LoadDefaultConfig( context.TODO(), userAgentLoadOption, config.WithRegion(region), config.WithEndpointDiscovery(aws.EndpointDiscoveryEnabled), ) if err != nil { return nil, err } return defaultClientFactory.NewClientWithOptions(Options{Config: awsConfig}), nil } // NewClientFromRegion uses the region to create the client func (defaultClientFactory DefaultClientFactory) NewClientFromRegion(region string) Client { awsConfig, err := config.LoadDefaultConfig( context.TODO(), userAgentLoadOption, config.WithRegion(region), ) if err != nil { panic(err) } return defaultClientFactory.NewClientWithOptions(Options{ Config: awsConfig, }) } // NewClient Create new client with AWS Config func (defaultClientFactory DefaultClientFactory) NewClient(awsConfig aws.Config) Client { return defaultClientFactory.NewClientWithOptions(Options{Config: awsConfig}) } // NewClientWithOptions Create new client with Options func (defaultClientFactory DefaultClientFactory) NewClientWithOptions(opts Options) Client { // The ECR Public API is only available in us-east-1 today publicConfig := opts.Config.Copy() publicConfig.Region = "us-east-1" return &defaultClient{ ecrClient: ecr.NewFromConfig(opts.Config), ecrPublicClient: ecrpublic.NewFromConfig(publicConfig), credentialCache: cache.BuildCredentialsCache(opts.Config, opts.CacheDir), } } ecr-login/api/mocks/000077500000000000000000000000001443273566100146355ustar00rootroot00000000000000ecr-login/api/mocks/api_mocks.go000066400000000000000000000031061443273566100171310ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. // Automatically generated by MockGen. DO NOT EDIT! // Source: github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api (interfaces: ECRAPI) package mock_api import ( "context" "github.com/aws/aws-sdk-go-v2/service/ecr" "github.com/aws/aws-sdk-go-v2/service/ecrpublic" ) type MockECRAPI struct { GetAuthorizationTokenFn func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) } func (m MockECRAPI) GetAuthorizationToken(_ context.Context, input *ecr.GetAuthorizationTokenInput, _ ...func(*ecr.Options)) (*ecr.GetAuthorizationTokenOutput, error) { return m.GetAuthorizationTokenFn(input) } type MockECRPublicAPI struct { GetAuthorizationTokenFn func(input *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) } func (m MockECRPublicAPI) GetAuthorizationToken(_ context.Context, input *ecrpublic.GetAuthorizationTokenInput, _ ...func(*ecrpublic.Options)) (*ecrpublic.GetAuthorizationTokenOutput, error) { return m.GetAuthorizationTokenFn(input) } ecr-login/cache/000077500000000000000000000000001443273566100140135ustar00rootroot00000000000000ecr-login/cache/build.go000066400000000000000000000047001443273566100154420ustar00rootroot00000000000000// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "context" "crypto/md5" "encoding/base64" "fmt" "os" "github.com/aws/aws-sdk-go-v2/aws" "github.com/mitchellh/go-homedir" "github.com/sirupsen/logrus" ecrconfig "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/config" ) func BuildCredentialsCache(config aws.Config, cacheDir string) CredentialsCache { if os.Getenv("AWS_ECR_DISABLE_CACHE") != "" { logrus.Debug("Cache disabled due to AWS_ECR_DISABLE_CACHE") return NewNullCredentialsCache() } if cacheDir == "" { //Get cacheDir from env var "AWS_ECR_CACHE_DIR" or set to default cacheDir = ecrconfig.GetCacheDir() } cacheDir, err := homedir.Expand(cacheDir) if err != nil { logrus.WithError(err).Debug("Could not expand cache path, disabling cache") return NewNullCredentialsCache() } cacheFilename := "cache.json" credentials, err := config.Credentials.Retrieve(context.TODO()) if err != nil { logrus.WithError(err).Debug("Could not fetch credentials for cache prefix, disabling cache") return NewNullCredentialsCache() } return NewFileCredentialsCache(cacheDir, cacheFilename, credentialsCachePrefix(config.Region, credentials), credentialsPublicCacheKey(credentials)) } // Determine a key prefix for a credentials cache. Because auth tokens are scoped to an account and region, rely on provided // region, as well as hash of the access key. func credentialsCachePrefix(region string, credentials aws.Credentials) string { return fmt.Sprintf("%s-%s-", region, checksum(credentials.AccessKeyID)) } func credentialsPublicCacheKey(credentials aws.Credentials) string { return fmt.Sprintf("%s-%s", ServiceECRPublic, checksum(credentials.AccessKeyID)) } // Base64 encodes an MD5 checksum. Relied on for uniqueness, and not for cryptographic security. func checksum(text string) string { hasher := md5.New() data := hasher.Sum([]byte(text)) return base64.StdEncoding.EncodeToString(data) } ecr-login/cache/build_test.go000066400000000000000000000043071443273566100165040ustar00rootroot00000000000000// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "fmt" "os" "testing" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/stretchr/testify/assert" ) const ( testRegion = "test-region" testCacheFilename = "cache.json" testAccessKey = "accessKey" testSecretKey = "secretKey" testToken = "token" // base64 MD5 sum of Credentials struct testCredentialHash = "YWNjZXNzS2V51B2M2Y8AsgTpgAmY7PhCfg==" ) func TestFactoryBuildFileCache(t *testing.T) { config := aws.Config{ Region: testRegion, Credentials: credentials.NewStaticCredentialsProvider(testAccessKey, testSecretKey, testToken), } cache := BuildCredentialsCache(config, "") assert.NotNil(t, cache) fileCache, ok := cache.(*fileCredentialCache) assert.True(t, ok, "built cache is not a fileCredentialsCache") assert.Equal(t, fileCache.cachePrefixKey, fmt.Sprintf("%s-%s-", testRegion, testCredentialHash)) assert.Equal(t, fileCache.filename, testCacheFilename) } func TestFactoryBuildNullCacheWithoutCredentials(t *testing.T) { config := aws.Config{ Region: testRegion, Credentials: aws.AnonymousCredentials{}, } cache := BuildCredentialsCache(config, "") assert.NotNil(t, cache) _, ok := cache.(*nullCredentialsCache) assert.True(t, ok, "built cache is a nullCredentialsCache") } func TestFactoryBuildNullCache(t *testing.T) { os.Setenv("AWS_ECR_DISABLE_CACHE", "1") defer os.Setenv("AWS_ECR_DISABLE_CACHE", "1") config := aws.Config{Region: testRegion} cache := BuildCredentialsCache(config, "") assert.NotNil(t, cache) _, ok := cache.(*nullCredentialsCache) assert.True(t, ok, "built cache is a nullCredentialsCache") } ecr-login/cache/credentials.go000066400000000000000000000026071443273566100166440ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "time" ) type CredentialsCache interface { Get(registry string) *AuthEntry GetPublic() *AuthEntry Set(registry string, entry *AuthEntry) List() []*AuthEntry Clear() } type Service string const ( ServiceECR Service = "ecr" ServiceECRPublic Service = "ecr-public" ) type AuthEntry struct { AuthorizationToken string RequestedAt time.Time ExpiresAt time.Time ProxyEndpoint string Service Service } // IsValid checks if AuthEntry is still valid at testTime. AuthEntries expire at 1/2 of their original // requested window. func (authEntry *AuthEntry) IsValid(testTime time.Time) bool { validWindow := authEntry.ExpiresAt.Sub(authEntry.RequestedAt) refreshTime := authEntry.ExpiresAt.Add(-1 * validWindow / time.Duration(2)) return testTime.Before(refreshTime) } ecr-login/cache/credentials_test.go000066400000000000000000000033221443273566100176760ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestIsValid_NewEntry(t *testing.T) { authEntry := &AuthEntry{ RequestedAt: time.Now(), ExpiresAt: time.Now().Add(12 * time.Hour), } assert.True(t, authEntry.IsValid(time.Now())) } func TestIsValid_OldEntry(t *testing.T) { authEntry := &AuthEntry{ RequestedAt: time.Now().Add(-12 * time.Hour), ExpiresAt: time.Now(), } assert.False(t, authEntry.IsValid(time.Now())) } func TestIsValid_BeforeRefreshTime(t *testing.T) { now := time.Now() authEntry := &AuthEntry{ RequestedAt: now.Add(-6 * time.Hour), ExpiresAt: now.Add(6 * time.Hour), } assert.True(t, authEntry.IsValid(now.Add(-1*time.Second))) } func TestIsValid_AtRefreshTime(t *testing.T) { now := time.Now() authEntry := &AuthEntry{ RequestedAt: now.Add(-6 * time.Hour), ExpiresAt: now.Add(6 * time.Hour), } assert.False(t, authEntry.IsValid(now)) } func TestIsValid_AfterRefreshTime(t *testing.T) { now := time.Now() authEntry := &AuthEntry{ RequestedAt: now.Add(-6 * time.Hour), ExpiresAt: now.Add(6 * time.Hour), } assert.False(t, authEntry.IsValid(now.Add(time.Second))) } ecr-login/cache/file.go000066400000000000000000000122421443273566100152620ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" "github.com/sirupsen/logrus" ) const registryCacheVersion = "1.0" type RegistryCache struct { Registries map[string]*AuthEntry Version string } type fileCredentialCache struct { path string filename string cachePrefixKey string publicCacheKey string } func newRegistryCache() *RegistryCache { return &RegistryCache{ Registries: make(map[string]*AuthEntry), Version: registryCacheVersion, } } // NewFileCredentialsCache returns a new file credentials cache. // // path is used for temporary files during save, and filename should be a relative filename // in the same directory where the cache is serialized and deserialized. // // cachePrefixKey is used for scoping credentials for a given credential cache (i.e. region and // accessKey). func NewFileCredentialsCache(path string, filename string, cachePrefixKey string, publicCacheKey string) CredentialsCache { if _, err := os.Stat(path); err != nil { os.MkdirAll(path, 0700) } return &fileCredentialCache{ path: path, filename: filename, cachePrefixKey: cachePrefixKey, publicCacheKey: publicCacheKey, } } func (f *fileCredentialCache) Get(registry string) *AuthEntry { logrus.WithField("registry", registry).Debug("Checking file cache") registryCache := f.init() return registryCache.Registries[f.cachePrefixKey+registry] } func (f *fileCredentialCache) GetPublic() *AuthEntry { logrus.Debug("Checking file cache for ECR Public") registryCache := f.init() return registryCache.Registries[f.publicCacheKey] } func (f *fileCredentialCache) Set(registry string, entry *AuthEntry) { logrus. WithField("registry", registry). WithField("service", entry.Service). Debug("Saving credentials to file cache") registryCache := f.init() key := f.cachePrefixKey + registry if entry.Service == ServiceECRPublic { key = f.publicCacheKey } registryCache.Registries[key] = entry err := f.save(registryCache) if err != nil { logrus.WithError(err).Info("Could not save cache") } } // List returns all of the available AuthEntries (regardless of prefix) func (f *fileCredentialCache) List() []*AuthEntry { registryCache := f.init() // optimize allocation for copy entries := make([]*AuthEntry, 0, len(registryCache.Registries)) for _, entry := range registryCache.Registries { entries = append(entries, entry) } return entries } func (f *fileCredentialCache) Clear() { err := os.Remove(f.fullFilePath()) if err != nil { logrus.WithError(err).Info("Could not clear cache") } } func (f *fileCredentialCache) fullFilePath() string { return filepath.Join(f.path, f.filename) } // Saves credential cache to disk. This writes to a temporary file first, then moves the file to the config location. // This eliminates from reading partially written credential files, and reduces (but does not eliminate) concurrent // file access. There is not guarantee here for handling multiple writes at once since there is no out of process locking. func (f *fileCredentialCache) save(registryCache *RegistryCache) error { file, err := ioutil.TempFile(f.path, ".config.json.tmp") if err != nil { return err } buff, err := json.MarshalIndent(registryCache, "", " ") if err != nil { file.Close() os.Remove(file.Name()) return err } _, err = file.Write(buff) if err != nil { file.Close() os.Remove(file.Name()) return err } file.Close() // note this is only atomic when relying on linux syscalls os.Rename(file.Name(), f.fullFilePath()) return err } func (f *fileCredentialCache) init() *RegistryCache { registryCache, err := f.load() if err != nil { logrus.WithError(err).Info("Could not load existing cache") f.Clear() registryCache = newRegistryCache() } return registryCache } // Loading a cache from disk will return errors for malformed or incompatible cache files. func (f *fileCredentialCache) load() (*RegistryCache, error) { registryCache := newRegistryCache() file, err := os.Open(f.fullFilePath()) if os.IsNotExist(err) { return registryCache, nil } if err != nil { return nil, err } defer file.Close() if err = json.NewDecoder(file).Decode(®istryCache); err != nil { return nil, err } if registryCache.Version != registryCacheVersion { return nil, fmt.Errorf("ecr: Registry cache version %#v is not compatible with %#v, ignoring existing cache", registryCache.Version, registryCacheVersion) } // migrate entries for key := range registryCache.Registries { if registryCache.Registries[key].Service == "" { registryCache.Registries[key].Service = ServiceECR } } return registryCache, nil } ecr-login/cache/file_test.go000066400000000000000000000102261443273566100163210ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" ) const ( testRegistryName = "testRegistry" testCachePrefixKey = "prefix-" testPublicCacheKey = "public-" testFilename = "test.json" ) var ( testAuthEntry = AuthEntry{ AuthorizationToken: "testToken", RequestedAt: time.Now().Add(-5 * time.Hour), ExpiresAt: time.Now().Add(7 * time.Hour), ProxyEndpoint: "testEndpoint", Service: ServiceECR, } testPublicAuthEntry = AuthEntry{ AuthorizationToken: "testToken", RequestedAt: time.Now().Add(-5 * time.Hour), ExpiresAt: time.Now().Add(7 * time.Hour), ProxyEndpoint: "testEndpoint", Service: ServiceECRPublic, } testPath = os.TempDir() + "/ecr" testFullFillename = filepath.Join(testPath, testFilename) ) func TestAuthEntryValid(t *testing.T) { assert.True(t, testAuthEntry.IsValid(time.Now())) } func TestAuthEntryInValid(t *testing.T) { assert.True(t, testAuthEntry.IsValid(time.Now().Add(time.Second))) } func TestCredentials(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey) credentialCache.Set(testRegistryName, &testAuthEntry) entry := credentialCache.Get(testRegistryName) assert.Equal(t, testAuthEntry.AuthorizationToken, entry.AuthorizationToken) assert.Equal(t, testAuthEntry.ProxyEndpoint, entry.ProxyEndpoint) assert.WithinDuration(t, testAuthEntry.RequestedAt, entry.RequestedAt, 1*time.Second) assert.WithinDuration(t, testAuthEntry.ExpiresAt, entry.ExpiresAt, 1*time.Second) assert.Equal(t, testAuthEntry.Service, entry.Service) entries := credentialCache.List() assert.NotEmpty(t, entries) assert.Len(t, entries, 1) assert.Equal(t, entry, entries[0]) credentialCache.Clear() entry = credentialCache.Get(testRegistryName) assert.Nil(t, entry) } func TestCredentialsPublic(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey) credentialCache.Set(testRegistryName, &testPublicAuthEntry) entry := credentialCache.GetPublic() assert.Equal(t, testPublicAuthEntry.AuthorizationToken, entry.AuthorizationToken) assert.Equal(t, testPublicAuthEntry.ProxyEndpoint, entry.ProxyEndpoint) assert.WithinDuration(t, testPublicAuthEntry.RequestedAt, entry.RequestedAt, 1*time.Second) assert.WithinDuration(t, testPublicAuthEntry.ExpiresAt, entry.ExpiresAt, 1*time.Second) assert.Equal(t, testPublicAuthEntry.Service, entry.Service) entries := credentialCache.List() assert.NotEmpty(t, entries) assert.Len(t, entries, 1) assert.Equal(t, entry, entries[0]) credentialCache.Clear() entry = credentialCache.GetPublic() assert.Nil(t, entry) } func TestPreviousVersionCache(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey) registryCache := newRegistryCache() registryCache.Version = "0.1" registryCache.Registries[testRegistryName] = &testAuthEntry credentialCache.(*fileCredentialCache).save(registryCache) entry := credentialCache.Get(testRegistryName) assert.Nil(t, entry) credentialCache.Clear() } const testBadJson = "{nope not good json at all." func TestInvalidCache(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey) file, err := os.Create(testFullFillename) assert.NoError(t, err) file.WriteString(testBadJson) err = file.Close() assert.NoError(t, err) entry := credentialCache.Get(testRegistryName) assert.Nil(t, entry) credentialCache.Clear() } ecr-login/cache/mocks/000077500000000000000000000000001443273566100151275ustar00rootroot00000000000000ecr-login/cache/mocks/cache_mocks.go000066400000000000000000000026031443273566100177160ustar00rootroot00000000000000// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package mock_cache import ( "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache" ) type MockCredentialsCache struct { GetFn func(registry string) *cache.AuthEntry GetPublicFn func() *cache.AuthEntry SetFn func(registry string, entry *cache.AuthEntry) ListFn func() []*cache.AuthEntry ClearFn func() } var _ cache.CredentialsCache = (*MockCredentialsCache)(nil) func (m MockCredentialsCache) Get(registry string) *cache.AuthEntry { return m.GetFn(registry) } func (m MockCredentialsCache) GetPublic() *cache.AuthEntry { return m.GetPublicFn() } func (m MockCredentialsCache) Set(registry string, entry *cache.AuthEntry) { m.SetFn(registry, entry) } func (m MockCredentialsCache) List() []*cache.AuthEntry { return m.ListFn() } func (m MockCredentialsCache) Clear() { m.ClearFn() } ecr-login/cache/null.go000066400000000000000000000020201443273566100153060ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache type nullCredentialsCache struct{} func NewNullCredentialsCache() CredentialsCache { return &nullCredentialsCache{} } func (n *nullCredentialsCache) Get(_ string) *AuthEntry { return nil } func (n *nullCredentialsCache) GetPublic() *AuthEntry { return nil } func (n *nullCredentialsCache) Set(_ string, _ *AuthEntry) { } func (n *nullCredentialsCache) List() []*AuthEntry { return []*AuthEntry{} } func (n *nullCredentialsCache) Clear() { } ecr-login/cache/null_test.go000066400000000000000000000017731443273566100163630ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "testing" "github.com/stretchr/testify/assert" ) func TestNullCache(t *testing.T) { credentialCache := NewNullCredentialsCache() entry := credentialCache.Get(testRegistryName) assert.Nil(t, entry) credentialCache.Set(testRegistryName, &testAuthEntry) entry = credentialCache.Get(testRegistryName) assert.Nil(t, entry) credentialCache.Clear() entries := credentialCache.List() assert.Empty(t, entries) } ecr-login/cli/000077500000000000000000000000001443273566100135175ustar00rootroot00000000000000ecr-login/cli/docker-credential-ecr-login/000077500000000000000000000000001443273566100207535ustar00rootroot00000000000000ecr-login/cli/docker-credential-ecr-login/main.go000066400000000000000000000023711443273566100222310ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package main import ( "flag" "fmt" "os" ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/config" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/version" "github.com/docker/docker-credential-helpers/credentials" ) const banner = `amazon-ecr-credential-helper Version: %s Git commit: %s ` func main() { var versionFlag bool flag.BoolVar(&versionFlag, "v", false, "print version and exit") flag.Parse() // Exit safely when version is used if versionFlag { fmt.Printf(banner, version.Version, version.GitCommitSHA) os.Exit(0) } config.SetupLogger() credentials.Serve(ecr.NewECRHelper()) } ecr-login/config/000077500000000000000000000000001443273566100142155ustar00rootroot00000000000000ecr-login/config/cache_dir.go000066400000000000000000000013401443273566100164430ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package config import "os" func GetCacheDir() string { if cacheDir := os.Getenv("AWS_ECR_CACHE_DIR"); cacheDir != "" { return cacheDir } return "~/.ecr" } ecr-login/config/log.go000066400000000000000000000025341443273566100153310ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package config import ( "fmt" "os" "path/filepath" "github.com/mitchellh/go-homedir" "github.com/sirupsen/logrus" ) func SetupLogger() { logrusConfig() } func logrusConfig() { logdir, err := homedir.Expand(GetCacheDir() + "/log") if err != nil { fmt.Fprintf(os.Stderr, "log: failed to find directory: %v", err) logdir = os.TempDir() } // Clean the path to replace with OS-specific separators logdir = filepath.Clean(logdir) err = os.MkdirAll(logdir, os.ModeDir|0700) if err != nil { fmt.Fprintf(os.Stderr, "log: failed to create directory: %v", err) logdir = os.TempDir() } file, err := os.OpenFile(filepath.Join(logdir, "ecr-login.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664) if err != nil { return } logrus.SetLevel(logrus.DebugLevel) logrus.SetOutput(file) } ecr-login/ecr.go000066400000000000000000000067711443273566100140630ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package ecr import ( "errors" "fmt" "io" "github.com/sirupsen/logrus" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api" "github.com/docker/docker-credential-helpers/credentials" ) var notImplemented = errors.New("not implemented") type ECRHelper struct { clientFactory api.ClientFactory logger *logrus.Logger } type Option func(*ECRHelper) // WithClientFactory sets the ClientFactory used to make API requests. func WithClientFactory(clientFactory api.ClientFactory) Option { return func(e *ECRHelper) { e.clientFactory = clientFactory } } // WithLogger sets a new logger instance that writes to the given writer, // instead of the default writer which writes to stderr. // // This can be useful if callers want to redirect logging emitted by this tool // to another location. func WithLogger(w io.Writer) Option { return func(e *ECRHelper) { logger := logrus.New() logger.Out = w e.logger = logger } } // NewECRHelper returns a new ECRHelper with the given options to override // default behavior. func NewECRHelper(opts ...Option) *ECRHelper { e := &ECRHelper{ clientFactory: api.DefaultClientFactory{}, logger: logrus.StandardLogger(), } for _, o := range opts { o(e) } return e } // ensure ECRHelper adheres to the credentials.Helper interface var _ credentials.Helper = (*ECRHelper)(nil) func (ECRHelper) Add(creds *credentials.Credentials) error { // This does not seem to get called return notImplemented } func (ECRHelper) Delete(serverURL string) error { // This does not seem to get called return notImplemented } func (self ECRHelper) Get(serverURL string) (string, string, error) { registry, err := api.ExtractRegistry(serverURL) if err != nil { self.logger. WithError(err). WithField("serverURL", serverURL). Error("Error parsing the serverURL") return "", "", credentials.NewErrCredentialsNotFound() } var client api.Client if registry.FIPS { client, err = self.clientFactory.NewClientWithFipsEndpoint(registry.Region) if err != nil { self.logger.WithError(err).Error("Error resolving FIPS endpoint") return "", "", credentials.NewErrCredentialsNotFound() } } else { client = self.clientFactory.NewClientFromRegion(registry.Region) } auth, err := client.GetCredentials(serverURL) if err != nil { self.logger.WithError(err).Error("Error retrieving credentials") return "", "", credentials.NewErrCredentialsNotFound() } return auth.Username, auth.Password, nil } func (self ECRHelper) List() (map[string]string, error) { self.logger.Debug("Listing credentials") client := self.clientFactory.NewClientWithDefaults() auths, err := client.ListCredentials() if err != nil { self.logger.WithError(err).Error("Error listing credentials") return nil, fmt.Errorf("ecr: could not list credentials: %v", err) } result := map[string]string{} for _, auth := range auths { serverURL := auth.ProxyEndpoint result[serverURL] = auth.Username } return result, nil } ecr-login/ecr_test.go000066400000000000000000000070511443273566100151120ustar00rootroot00000000000000// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package ecr import ( "errors" "fmt" "testing" ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api" mock_api "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/mocks" "github.com/docker/docker-credential-helpers/credentials" "github.com/stretchr/testify/assert" ) const ( region = "us-east-1" proxyEndpoint = "123456789012" + ".dkr.ecr." + region + ".amazonaws.com" proxyEndpointUrl = "https://" + proxyEndpoint expectedUsername = "username" expectedPassword = "password" ) func TestGetSuccess(t *testing.T) { factory := &mock_api.MockClientFactory{} client := &mock_api.MockClient{} helper := NewECRHelper(WithClientFactory(factory)) factory.NewClientFromRegionFn = func(_ string) ecr.Client { return client } client.GetCredentialsFn = func(serverURL string) (*ecr.Auth, error) { if serverURL != proxyEndpoint { return nil, fmt.Errorf("unexpected input: %s", serverURL) } return &ecr.Auth{ Username: expectedUsername, Password: expectedPassword, ProxyEndpoint: proxyEndpointUrl, }, nil } username, password, err := helper.Get(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, expectedUsername, username) assert.Equal(t, expectedPassword, password) } func TestGetError(t *testing.T) { factory := &mock_api.MockClientFactory{} client := &mock_api.MockClient{} helper := NewECRHelper(WithClientFactory(factory)) factory.NewClientFromRegionFn = func(_ string) ecr.Client { return client } client.GetCredentialsFn = func(serverURL string) (*ecr.Auth, error) { return nil, errors.New("test error") } username, password, err := helper.Get(proxyEndpoint) assert.True(t, credentials.IsErrCredentialsNotFound(err)) assert.Empty(t, username) assert.Empty(t, password) } func TestGetNoMatch(t *testing.T) { helper := NewECRHelper(WithClientFactory(nil)) username, password, err := helper.Get("not-ecr-server-url") assert.True(t, credentials.IsErrCredentialsNotFound(err)) assert.Empty(t, username) assert.Empty(t, password) } func TestListSuccess(t *testing.T) { factory := &mock_api.MockClientFactory{} client := &mock_api.MockClient{} helper := NewECRHelper(WithClientFactory(factory)) factory.NewClientWithDefaultsFn = func() ecr.Client { return client } client.ListCredentialsFn = func() ([]*ecr.Auth, error) { return []*ecr.Auth{{ Username: expectedUsername, Password: expectedPassword, ProxyEndpoint: proxyEndpointUrl, }}, nil } serverList, err := helper.List() assert.NoError(t, err) assert.Len(t, serverList, 1) assert.Equal(t, expectedUsername, serverList[proxyEndpointUrl]) } func TestListFailure(t *testing.T) { factory := &mock_api.MockClientFactory{} client := &mock_api.MockClient{} helper := NewECRHelper(WithClientFactory(factory)) factory.NewClientWithDefaultsFn = func() ecr.Client { return client } client.ListCredentialsFn = func() ([]*ecr.Auth, error) { return nil, errors.New("nope") } serverList, err := helper.List() assert.Error(t, err) assert.Len(t, serverList, 0) } ecr-login/go.mod000066400000000000000000000010161443273566100140540ustar00rootroot00000000000000module github.com/awslabs/amazon-ecr-credential-helper/ecr-login require ( github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2/config v1.18.22 github.com/aws/aws-sdk-go-v2/credentials v1.13.21 github.com/aws/aws-sdk-go-v2/service/ecr v1.18.10 github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.16.1 github.com/aws/smithy-go v1.13.5 github.com/docker/docker-credential-helpers v0.7.0 github.com/mitchellh/go-homedir v1.1.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 ) go 1.13 ecr-login/go.sum000066400000000000000000000141641443273566100141110ustar00rootroot00000000000000github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.18.22 h1:7vkUEmjjv+giht4wIROqLs+49VWmiQMMHSduxmoNKLU= github.com/aws/aws-sdk-go-v2/config v1.18.22/go.mod h1:mN7Li1wxaPxSSy4Xkr6stFuinJGf3VZW3ZSNvO0q6sI= github.com/aws/aws-sdk-go-v2/credentials v1.13.21 h1:VRiXnPEaaPeGeoFcXvMZOB5K/yfIXOYE3q97Kgb0zbU= github.com/aws/aws-sdk-go-v2/credentials v1.13.21/go.mod h1:90Dk1lJoMyspa/EDUrldTxsPns0wn6+KpRKpdAWc0uA= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= github.com/aws/aws-sdk-go-v2/service/ecr v1.18.10 h1:3s6Jg0xx6U/wDVgZy8exuZoGlsL/6tYcItAaXg9vMSA= github.com/aws/aws-sdk-go-v2/service/ecr v1.18.10/go.mod h1:Ce1q2jlNm8BVpjLaOnwnm5v2RClAbK6txwPljFzyW6c= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.16.1 h1:iqooVPD/xAM5SCTbrFsBeuiQ2o0D9wdqlHcUBTDxJPA= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.16.1/go.mod h1:uHtRE7aqXNmpeYL+7Ec7LacH5zC9+w2T5MBOeEKDdu0= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 h1:GAiaQWuQhQQui76KjuXeShmyXqECwQ0mGRMc/rwsL+c= github.com/aws/aws-sdk-go-v2/service/sso v1.12.9/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 h1:TraLwncRJkWqtIBVKI/UqBymq4+hL+3MzUOtUATuzkA= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 h1:6UbNM/KJhMBfOI5+lpVcJ/8OA7cBSz0O6OX37SRKlSw= github.com/aws/aws-sdk-go-v2/service/sts v1.18.10/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ecr-login/mocks/000077500000000000000000000000001443273566100140645ustar00rootroot00000000000000ecr-login/mocks/ecr_mocks.go000066400000000000000000000044121443273566100163610ustar00rootroot00000000000000// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package mock_api import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api" ) type MockClientFactory struct { NewClientFn func(awsConfig aws.Config) api.Client NewClientWithOptionsFn func(opts api.Options) api.Client NewClientFromRegionFn func(region string) api.Client NewClientWithFipsEndpointFn func(region string) (api.Client, error) NewClientWithDefaultsFn func() api.Client } func (m MockClientFactory) NewClient(awsConfig aws.Config) api.Client { return m.NewClientFn(awsConfig) } func (m MockClientFactory) NewClientWithOptions(opts api.Options) api.Client { return m.NewClientWithOptionsFn(opts) } func (m MockClientFactory) NewClientFromRegion(region string) api.Client { return m.NewClientFromRegionFn(region) } func (m MockClientFactory) NewClientWithFipsEndpoint(region string) (api.Client, error) { return m.NewClientWithFipsEndpointFn(region) } func (m MockClientFactory) NewClientWithDefaults() api.Client { return m.NewClientWithDefaultsFn() } var _ api.ClientFactory = (*MockClientFactory)(nil) type MockClient struct { GetCredentialsFn func(serverURL string) (*api.Auth, error) GetCredentialsByRegistryIDFn func(registryID string) (*api.Auth, error) ListCredentialsFn func() ([]*api.Auth, error) } var _ api.Client = (*MockClient)(nil) func (m *MockClient) GetCredentials(serverURL string) (*api.Auth, error) { return m.GetCredentialsFn(serverURL) } func (m *MockClient) GetCredentialsByRegistryID(registryID string) (*api.Auth, error) { return m.GetCredentialsByRegistryIDFn(registryID) } func (m *MockClient) ListCredentials() ([]*api.Auth, error) { return m.ListCredentialsFn() } ecr-login/vendor/000077500000000000000000000000001443273566100142455ustar00rootroot00000000000000ecr-login/version/000077500000000000000000000000001443273566100144355ustar00rootroot00000000000000ecr-login/version/version.go000066400000000000000000000003151443273566100164500ustar00rootroot00000000000000package version // Version indicates which version of the binary is running. var Version = "development" // GitCommitSHA indicates which git shorthash the binary was built off of var GitCommitSHA string scripts/000077500000000000000000000000001443273566100125605ustar00rootroot00000000000000scripts/build_binary.sh000077500000000000000000000026321443273566100155650ustar00rootroot00000000000000#!/bin/bash # Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. set -euo pipefail # Normalize to working directory being build root (up one level from ./scripts) ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd ) cd "${ROOT}" # Builds the ecr-login binary from source in the specified destination paths. mkdir -p $1 cd "${ROOT}/ecr-login" package_root="github.com/awslabs/amazon-ecr-credential-helper/ecr-login" version_ldflags="" if [[ -n "${2}" ]]; then version_ldflags="-X ${package_root}/version.Version=${2}" fi if [[ -n "${3}" ]]; then version_ldflags="$version_ldflags -X ${package_root}/version.GitCommitSHA=${3}" fi GOOS=${TARGET_GOOS:-} GOARCH=${TARGET_GOARCH:-} CGO_ENABLED=0 \ go build \ -installsuffix cgo \ -a \ -ldflags "-buildid= -s ${version_ldflags}" \ -trimpath \ -o ../$1/docker-credential-ecr-login \ ./cli/docker-credential-ecr-login scripts/build_variant.sh000077500000000000000000000023161443273566100157440ustar00rootroot00000000000000#!/bin/bash # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. set -euo pipefail # This script is used for compilation of a specific variant. # Specify GOOS as $1, GOARCH as $2 # Binaries are placed into ./bin/$GOOS-$GOARCH/docker-credential-ecr-login # Normalize to working directory being build root (up one level from ./scripts) ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd ) cd "${ROOT}" # Export variables export TARGET_GOOS="$1" export TARGET_GOARCH="$2" ECR_LOGIN_VERSION="$3" ECR_LOGIN_GITCOMMIT_SHA="$4" ./scripts/build_binary.sh "./bin/${TARGET_GOOS}-${TARGET_GOARCH}" $ECR_LOGIN_VERSION $ECR_LOGIN_GITCOMMIT_SHA echo "Built ecr-login for ${TARGET_GOOS}-${TARGET_GOARCH}-${ECR_LOGIN_VERSION}"scripts/gogenerate000077500000000000000000000014351443273566100146310ustar00rootroot00000000000000#!/bin/bash # Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. set -e # Normalize to working directory being build root (up one level from ./scripts) ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd ) cd "${ROOT}" go generate -x $(go list ./ecr-login/... | grep -v '/vendor/') scripts/hack/000077500000000000000000000000001443273566100134665ustar00rootroot00000000000000scripts/hack/codepipeline-git-commit.sh000077500000000000000000000015111443273566100205320ustar00rootroot00000000000000#!/bin/bash # Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. # This script populates the GITCOMMIT_SHA file when built inside AWS CodeBuild # and invoked from AWS CodePipeline. set -ex [[ -z "${CODEBUILD_RESOLVED_SOURCE_VERSION}" ]] && exit 1 echo "${CODEBUILD_RESOLVED_SOURCE_VERSION}" > GITCOMMIT_SHA cat GITCOMMIT_SHA scripts/hack/codepipeline-source-archive.sh000077500000000000000000000025751443273566100214130ustar00rootroot00000000000000#!/bin/bash # Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. # This script checks out a git repository with the correct revision for # execution in AWS CodeBuild when triggered by AWS CodePipeline and produces a # source archive. # The script assumes that you have the following environment variables set: # * CODEBUILD_RESOLVED_SOURCE_VERSION - Set automatically when AWS CodePipeline # invokes AWS CodeBuild # * GIT_REMOTE - A valid git remote that will be cloned # This script is meant to be triggered only from branch-based executions of the # pipeline. It does not handle pull requests. set -ex [[ -d .git ]] && exit 1 [[ -z "${CODEBUILD_RESOLVED_SOURCE_VERSION}" ]] && exit 1 [[ -z "${GIT_REMOTE}" ]] && exit 1 mkdir archive git clone "${GIT_REMOTE}" archive cd archive git checkout "${CODEBUILD_RESOLVED_SOURCE_VERSION}" make release-tarball make release-tarball-no-vendor scripts/hack/symlink-gopath-codebuild.sh000077500000000000000000000016271443273566100207310ustar00rootroot00000000000000#!/bin/bash # Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. # This script is meant to make the package available in the 'canonical' location # for execution in AWS CodeBuild in the golang container image. if [[ ! -d "/go/src/github.com/awslabs/amazon-ecr-credential-helper" ]]; then mkdir -p "/go/src/github.com/awslabs" ln -s "$(pwd)" "/go/src/github.com/awslabs/amazon-ecr-credential-helper" fiscripts/hack/version-changelog.sh000077500000000000000000000027221443273566100174420ustar00rootroot00000000000000#!/bin/bash # Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. # This script is an incredibly simple parser for the CHANGELOG.md in this # repository. It expects the changelog to have the following format: # # * Sections delimited by "#" # * Section titles matching the VERSION file # # For example, consider the following sample changelog: # # # 1.2.3 # * Foo # # 0.1.2-alpha # * Bar # # If the VERSION file is set to "0.1.2-alpha", the output of this script is: # # # 0.1.2-alpha # * Bar set -e # Normalize to working directory being source root (up two levels) ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd ) cd "${ROOT}" VERSION="$(cat VERSION)" match="" while IFS= read -r line; do if [[ "${line:0:1}" == "#" ]]; then if [[ "${line}" == "# ${VERSION}" ]]; then match="y" continue else match="" fi fi if [[ -n "${match}" ]]; then echo "$line" fi done < "CHANGELOG.md" GITCOMMIT_SHA0000644000000000000000000000001014432747332013026 0ustar00rootroot00000000000000adf1baf