Skip to content

Commit

Permalink
Read access token to use it in authentication. (#12123)
Browse files Browse the repository at this point in the history
* Read access token to use it in authentication.

* Extract building new authenticator to separate function. The authenticator passed to the sync function already contains key or token.

* formating

* Log finished important steps

* Add suite test file to have only one entry point in to tests.

* Added missing suite test file.
  • Loading branch information
dekiel authored Oct 16, 2024
1 parent d3a2e3e commit 5e3fb3e
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 29 deletions.
13 changes: 13 additions & 0 deletions cmd/image-syncer/image_syncer_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestImageSyncer(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "ImageSyncer Suite")
}
63 changes: 43 additions & 20 deletions cmd/image-syncer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,7 @@ func SyncImage(ctx context.Context, src, dest string, dryRun bool, auth authn.Au
}

// SyncImages is a main syncing function that takes care of copying images.
func SyncImages(ctx context.Context, cfg *Config, images *imagesync.SyncDef, authCfg []byte) error {
var auth authn.Authenticator
if cfg.TargetKeyFile != "" {
auth = &authn.Basic{Username: "_json_key", Password: string(authCfg)}
} else {
auth = &authn.Bearer{Token: string(authCfg)}
}
func SyncImages(ctx context.Context, cfg *Config, images *imagesync.SyncDef, auth authn.Authenticator) error {
for _, img := range images.Images {
target, err := getTarget(img.Source, cfg.TargetRepoPrefix, img.Tag)
imageType := "Index"
Expand Down Expand Up @@ -224,6 +218,40 @@ func SyncImages(ctx context.Context, cfg *Config, images *imagesync.SyncDef, aut
return nil
}

// newAuthenticator creates a new authenticator based on the provided configuration
// An authenticator is used to authenticate to the target repository.
func (cfg *Config) newAuthenticator() (authn.Authenticator, error) {
log.Debug("started creating new authenticator")
var (
auth authn.Authenticator
authCfg []byte
err error
)

if cfg.TargetKeyFile != "" {
log.WithField("targetKeyFile", cfg.TargetKeyFile).Debug("target key file path provided, reading the file")

authCfg, err = os.ReadFile(cfg.TargetKeyFile)
if err != nil {
return nil, fmt.Errorf("could not open target auth key JSON file, error: %w", err)
}
log.Debug("target key file read successfully, creating basic authenticator")

auth = &authn.Basic{Username: "_json_key", Password: string(authCfg)}
log.WithField("username", "_json_key").Debug("basic authenticator created successfully")

return auth, nil
}
if cfg.AccessToken != "" {
log.Debug("access token provided, creating bearer authenticator")
auth = &authn.Bearer{Token: cfg.AccessToken}

return auth, nil
}

return nil, fmt.Errorf("no target auth key file or access token provided")
}

func main() {
log.Out = os.Stdout
var cfg Config
Expand All @@ -234,7 +262,6 @@ func main() {
Long: `image-syncer copies docker images. It compares checksum between source and target and protects target images against overriding`,
//nolint:revive
Run: func(cmd *cobra.Command, args []string) {
var authCfg []byte
logLevel := logrus.InfoLevel
if cfg.Debug {
logLevel = logrus.DebugLevel
Expand All @@ -248,26 +275,22 @@ func main() {
if err != nil {
log.WithError(err).Fatal("Could not parse images file")
}
if cfg.TargetKeyFile != "" {
authCfg, err = os.ReadFile(cfg.TargetKeyFile)
if err != nil {
log.WithError(err).Fatal("Could not open target auth key JSON")
}
log.Info("Parsed images file")

auth, err := cfg.newAuthenticator()
if err != nil {
log.WithError(err).Fatal("Failed to create authenticator")
}
log.Info("Created authenticator for target repository")

if cfg.DryRun {
log.Info("Dry-Run enabled. Program will not make any changes to the target repository.")
}

// This error looks like some leftover.
if err != nil {
log.WithError(err).Fatal("Failed to create signer instance")
}
if err := SyncImages(ctx, &cfg, imagesFile, authCfg); err != nil {
if err := SyncImages(ctx, &cfg, imagesFile, auth); err != nil {
log.WithError(err).Fatal("Failed to sync images")
} else {
log.Info("All images synced successfully")
}
log.Info("All images synced successfully")
},
}

Expand Down
55 changes: 55 additions & 0 deletions cmd/image-syncer/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

import (
"github.com/google/go-containerregistry/pkg/authn"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("newAuthenticator", func() {
var cfg Config

BeforeEach(func() {
cfg = Config{}
})

Context("when TargetKeyFile is provided", func() {
It("should create a basic authenticator", func() {
cfg.TargetKeyFile = "./test-fixtures/keyfile.json"
auth, err := cfg.newAuthenticator()
Expect(err).NotTo(HaveOccurred())
Expect(auth).To(Equal(&authn.Basic{
Username: "_json_key",
Password: "test_content",
}))
})

It("should return an error if the key file cannot be read", func() {
cfg.TargetKeyFile = "./test-fixtures/invalid_keyfile.json"
auth, err := cfg.newAuthenticator()
Expect(err).To(HaveOccurred())
Expect(auth).To(BeNil())
})
})

Context("when AccessToken is provided", func() {
It("should create a bearer authenticator", func() {
cfg.AccessToken = "valid-access-token"
auth, err := cfg.newAuthenticator()
Expect(err).NotTo(HaveOccurred())
Expect(auth).To(Equal(&authn.Bearer{
Token: "valid-access-token",
}))
})
})

Context("when neither TargetKeyFile nor AccessToken is provided", func() {
It("should return an error", func() {
cfg.TargetKeyFile = ""
cfg.AccessToken = ""
auth, err := cfg.newAuthenticator()
Expect(err).To(HaveOccurred())
Expect(auth).To(BeNil())
})
})
})
1 change: 1 addition & 0 deletions cmd/image-syncer/test-fixtures/keyfile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test_content
6 changes: 0 additions & 6 deletions cmd/image-syncer/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,11 @@ package main

import (
"os"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestMainSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Main Suite")
}

var _ = Describe("getTarget", func() {
tests := []struct {
source string
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ require (
github.com/jamiealquiza/envy v1.1.0
github.com/jinzhu/copier v0.4.0
github.com/microsoft/azure-devops-go-api/azuredevops/v7 v7.1.0
github.com/onsi/ginkgo/v2 v2.20.2
github.com/onsi/ginkgo/v2 v2.20.1
github.com/onsi/gomega v1.34.2
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,8 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo=
github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
Expand Down

0 comments on commit 5e3fb3e

Please sign in to comment.