Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions card.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
Expand Down Expand Up @@ -70,7 +69,7 @@ func writeCard(path string, card interface{}) {
case path == "/dev/stderr":
writeCardTo(os.Stderr, data)
case path != "":
ioutil.WriteFile(path, data, 0644)
os.WriteFile(path, data, 0644)
}
}

Expand Down
3 changes: 1 addition & 2 deletions cmd/drone-acr/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
Expand Down Expand Up @@ -238,7 +237,7 @@ func setupACRCert(cert, certPath string) error {
if err != nil {
return errors.Wrap(err, "failed to base64 decode ACR certificate")
}
err = ioutil.WriteFile(certPath, decoded, 0644)
err = os.WriteFile(certPath, decoded, 0644)
if err != nil {
return errors.Wrap(err, "failed to write ACR certificate")
}
Expand Down
147 changes: 91 additions & 56 deletions cmd/drone-ecr/main.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package main

import (
"context"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
Expand All @@ -13,17 +13,18 @@ import (
"github.com/joho/godotenv"
"github.com/sirupsen/logrus"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/aws/aws-sdk-go-v2/service/ecr/types"
"github.com/aws/aws-sdk-go-v2/service/sts"

docker "github.com/drone-plugins/drone-docker"
)

type ecrAPI interface {
DescribeImages(*ecr.DescribeImagesInput) (*ecr.DescribeImagesOutput, error)
DescribeImages(ctx context.Context, params *ecr.DescribeImagesInput, optFns ...func(*ecr.Options)) (*ecr.DescribeImagesOutput, error)
}

const defaultRegion = "us-east-1"
Expand Down Expand Up @@ -62,13 +63,14 @@ func main() {
os.Setenv("AWS_SECRET_ACCESS_KEY", secret)
}

sess, err := session.NewSession(&aws.Config{Region: &region})
ctx := context.Background()
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region))
if err != nil {
log.Fatal(fmt.Sprintf("error creating aws session: %v", err))
log.Fatal(fmt.Sprintf("error creating aws config: %v", err))
}

svc := getECRClient(sess, assumeRole, externalId, idToken)
username, password, defaultRegistry, err := getAuthInfo(svc)
svc := getECRClient(ctx, cfg, assumeRole, externalId, idToken)
username, password, defaultRegistry, err := getAuthInfo(ctx, svc)

if registry == "" {
registry = defaultRegistry
Expand All @@ -83,32 +85,32 @@ func main() {
}

if create {
err = ensureRepoExists(svc, trimHostname(repo, registry), scanOnPush)
err = ensureRepoExists(ctx, svc, trimHostname(repo, registry), scanOnPush)
if err != nil {
log.Fatal(fmt.Sprintf("error creating ECR repo: %v", err))
}
err = updateImageScannningConfig(svc, trimHostname(repo, registry), scanOnPush)
err = updateImageScannningConfig(ctx, svc, trimHostname(repo, registry), scanOnPush)
if err != nil {
log.Fatal(fmt.Sprintf("error updating scan on push for ECR repo: %v", err))
}
}

if lifecyclePolicy != "" {
p, err := ioutil.ReadFile(lifecyclePolicy)
p, err := os.ReadFile(lifecyclePolicy)
if err != nil {
log.Fatal(err)
}
if err := uploadLifeCyclePolicy(svc, string(p), trimHostname(repo, registry)); err != nil {
if err := uploadLifeCyclePolicy(ctx, svc, string(p), trimHostname(repo, registry)); err != nil {
log.Fatal(fmt.Sprintf("error uploading ECR lifecycle policy: %v", err))
}
}

if repositoryPolicy != "" {
p, err := ioutil.ReadFile(repositoryPolicy)
p, err := os.ReadFile(repositoryPolicy)
if err != nil {
log.Fatal(err)
}
if err := uploadRepositoryPolicy(svc, string(p), trimHostname(repo, registry)); err != nil {
if err := uploadRepositoryPolicy(ctx, svc, string(p), trimHostname(repo, registry)); err != nil {
log.Fatal(fmt.Sprintf("error uploading ECR repository policy. %v", err))
}
}
Expand Down Expand Up @@ -136,7 +138,7 @@ func main() {

repositoryName := trimHostname(repo, registry)
for _, t := range tags {
exists, err := tagExists(svc, repositoryName, t)
exists, err := tagExists(ctx, svc, repositoryName, t)
if err != nil {
logrus.Fatalf("Error checking if image exists for tag %s: %v", t, err)
}
Expand All @@ -162,13 +164,18 @@ func trimHostname(repo, registry string) string {
return repo
}

func ensureRepoExists(svc *ecr.ECR, name string, scanOnPush bool) (err error) {
input := &ecr.CreateRepositoryInput{}
input.SetRepositoryName(name)
input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush})
_, err = svc.CreateRepository(input)
func ensureRepoExists(ctx context.Context, svc *ecr.Client, name string, scanOnPush bool) (err error) {
input := &ecr.CreateRepositoryInput{
RepositoryName: aws.String(name),
ImageScanningConfiguration: &types.ImageScanningConfiguration{
ScanOnPush: scanOnPush,
},
}
_, err = svc.CreateRepository(ctx, input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ecr.ErrCodeRepositoryAlreadyExistsException {
// Check if repository already exists
var repoExistsErr *types.RepositoryAlreadyExistsException
if isErrorType(err, &repoExistsErr) {
// eat it, we skip checking for existing to save two requests
err = nil
}
Expand All @@ -177,38 +184,61 @@ func ensureRepoExists(svc *ecr.ECR, name string, scanOnPush bool) (err error) {
return
}

func updateImageScannningConfig(svc *ecr.ECR, name string, scanOnPush bool) (err error) {
input := &ecr.PutImageScanningConfigurationInput{}
input.SetRepositoryName(name)
input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush})
_, err = svc.PutImageScanningConfiguration(input)
func isErrorType[T error](err error, target *T) bool {
if err == nil {
return false
}
for err != nil {
if matched, ok := err.(T); ok {
*target = matched
return true
}
if unwrapper, ok := err.(interface{ Unwrap() error }); ok {
err = unwrapper.Unwrap()
} else {
break
}
}
return false
}

func updateImageScannningConfig(ctx context.Context, svc *ecr.Client, name string, scanOnPush bool) (err error) {
input := &ecr.PutImageScanningConfigurationInput{
RepositoryName: aws.String(name),
ImageScanningConfiguration: &types.ImageScanningConfiguration{
ScanOnPush: scanOnPush,
},
}
_, err = svc.PutImageScanningConfiguration(ctx, input)

return err
}

func uploadLifeCyclePolicy(svc *ecr.ECR, lifecyclePolicy string, name string) (err error) {
input := &ecr.PutLifecyclePolicyInput{}
input.SetLifecyclePolicyText(lifecyclePolicy)
input.SetRepositoryName(name)
_, err = svc.PutLifecyclePolicy(input)
func uploadLifeCyclePolicy(ctx context.Context, svc *ecr.Client, lifecyclePolicy string, name string) (err error) {
input := &ecr.PutLifecyclePolicyInput{
LifecyclePolicyText: aws.String(lifecyclePolicy),
RepositoryName: aws.String(name),
}
_, err = svc.PutLifecyclePolicy(ctx, input)

return err
}

func uploadRepositoryPolicy(svc *ecr.ECR, repositoryPolicy string, name string) (err error) {
input := &ecr.SetRepositoryPolicyInput{}
input.SetPolicyText(repositoryPolicy)
input.SetRepositoryName(name)
_, err = svc.SetRepositoryPolicy(input)
func uploadRepositoryPolicy(ctx context.Context, svc *ecr.Client, repositoryPolicy string, name string) (err error) {
input := &ecr.SetRepositoryPolicyInput{
PolicyText: aws.String(repositoryPolicy),
RepositoryName: aws.String(name),
}
_, err = svc.SetRepositoryPolicy(ctx, input)

return err
}

func getAuthInfo(svc *ecr.ECR) (username, password, registry string, err error) {
func getAuthInfo(ctx context.Context, svc *ecr.Client) (username, password, registry string, err error) {
var result *ecr.GetAuthorizationTokenOutput
var decoded []byte

result, err = svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{})
result, err = svc.GetAuthorizationToken(ctx, &ecr.GetAuthorizationTokenInput{})
if err != nil {
return
}
Expand Down Expand Up @@ -247,11 +277,13 @@ func getenv(key ...string) (s string) {
return
}

func getECRClient(sess *session.Session, role string, externalId string, idToken string) *ecr.ECR {
func getECRClient(ctx context.Context, cfg aws.Config, role string, externalId string, idToken string) *ecr.Client {
if role == "" {
return ecr.New(sess)
return ecr.NewFromConfig(cfg)
}

stsClient := sts.NewFromConfig(cfg)

if idToken != "" {
tempFile, err := os.CreateTemp("/tmp", "idToken-*.jwt")
if err != nil {
Expand All @@ -268,31 +300,34 @@ func getECRClient(sess *session.Session, role string, externalId string, idToken
}

// Create credentials using the path to the ID token file
creds := stscreds.NewWebIdentityCredentials(sess, role, "", tempFile.Name())
return ecr.New(sess, &aws.Config{Credentials: creds})
creds := stscreds.NewWebIdentityRoleProvider(stsClient, role, stscreds.IdentityTokenFile(tempFile.Name()))
cfg.Credentials = aws.NewCredentialsCache(creds)
return ecr.NewFromConfig(cfg)
} else if externalId != "" {
return ecr.New(sess, &aws.Config{
Credentials: stscreds.NewCredentials(sess, role, func(p *stscreds.AssumeRoleProvider) {
p.ExternalID = &externalId
}),
creds := stscreds.NewAssumeRoleProvider(stsClient, role, func(o *stscreds.AssumeRoleOptions) {
o.ExternalID = &externalId
})
cfg.Credentials = aws.NewCredentialsCache(creds)
return ecr.NewFromConfig(cfg)
} else {
return ecr.New(sess, &aws.Config{
Credentials: stscreds.NewCredentials(sess, role),
})
creds := stscreds.NewAssumeRoleProvider(stsClient, role)
cfg.Credentials = aws.NewCredentialsCache(creds)
return ecr.NewFromConfig(cfg)
}
}

func tagExists(svc ecrAPI, repository, tag string) (bool, error) {
func tagExists(ctx context.Context, svc ecrAPI, repository, tag string) (bool, error) {
input := &ecr.DescribeImagesInput{
RepositoryName: aws.String(repository),
ImageIds: []*ecr.ImageIdentifier{
ImageIds: []types.ImageIdentifier{
{ImageTag: aws.String(tag)},
},
}
output, err := svc.DescribeImages(input)
output, err := svc.DescribeImages(ctx, input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok && aerr.Code() == "ImageNotFoundException" {
// Check if image not found
var imageNotFoundErr *types.ImageNotFoundException
if isErrorType(err, &imageNotFoundErr) {
return false, nil
}
return false, err
Expand Down
22 changes: 16 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ module github.com/drone-plugins/drone-docker
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2
github.com/aws/aws-sdk-go v1.26.7
github.com/aws/aws-sdk-go-v2 v1.32.6
github.com/aws/aws-sdk-go-v2/config v1.28.6
github.com/aws/aws-sdk-go-v2/credentials v1.17.47
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.6
github.com/aws/aws-sdk-go-v2/service/sts v1.33.2
github.com/coreos/go-semver v0.3.0
github.com/dchest/uniuri v1.2.0
github.com/drone-plugins/drone-plugin-lib v0.4.1
Expand All @@ -22,6 +26,15 @@ require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
Expand All @@ -31,7 +44,7 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand All @@ -44,10 +57,7 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

go 1.24

toolchain go1.24.11
go 1.25
Loading