Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow read-only creds for container registry login #1379

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ const (
ArgRegistry = "registry"
// ArgRegistryExpirySeconds indicates the length of time the token will be valid in seconds.
ArgRegistryExpirySeconds = "expiry-seconds"
// ArgRegistryReadOnly indicates that a generated registry API token should be read-only.
ArgRegistryReadOnly = "read-only"
// ArgSubscriptionTier is a subscription tier slug.
ArgSubscriptionTier = "subscription-tier"
// ArgGCIncludeUntaggedManifests indicates that a garbage collection should delete
Expand Down
9 changes: 8 additions & 1 deletion commands/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ func Registry() *Command {
loginRegDesc, Writer)
AddIntFlag(cmdRegistryLogin, doctl.ArgRegistryExpirySeconds, "", 0,
"The length of time the registry credentials will be valid for in seconds. By default, the credentials do not expire.")
AddBoolFlag(cmdRegistryLogin, doctl.ArgRegistryReadOnly, "", false,
"If true, the DigitalOcean API token generated by the login command will be read-only, causing any push operations to fail. By default, the API token is read-write.")

logoutRegDesc := "This command logs Docker out of the private container registry, revoking access to it."
cmdRunRegistryLogout := CmdBuilder(cmd, RunRegistryLogout, "logout", "Log out Docker from a container registry",
Expand Down Expand Up @@ -379,8 +381,13 @@ func RunRegistryLogin(c *CmdConfig) error {
if err != nil {
return err
}
readOnly, err := c.Doit.GetBool(c.NS, doctl.ArgRegistryReadOnly)
if err != nil {
return err
}

regCredReq := godo.RegistryDockerCredentialsRequest{
ReadWrite: true,
ReadWrite: !readOnly,
}
if expirySeconds != 0 {
regCredReq.ExpirySeconds = godo.Int(expirySeconds)
Expand Down
13 changes: 13 additions & 0 deletions commands/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@ func TestRegistryLogin(t *testing.T) {
tests := []struct {
name string
expirySeconds int
readOnly bool
expect func(m *mocks.MockRegistryService)
}{
{
Expand All @@ -662,6 +663,17 @@ func TestRegistryLogin(t *testing.T) {
}).Return(testDockerCredentials, nil)
},
},
{
name: "with-read-only",
expirySeconds: 0,
readOnly: true,
expect: func(m *mocks.MockRegistryService) {
m.EXPECT().Endpoint().Return(do.RegistryHostname)
m.EXPECT().DockerCredentials(&godo.RegistryDockerCredentialsRequest{
ReadWrite: false,
}).Return(testDockerCredentials, nil)
},
},
}

for _, test := range tests {
Expand All @@ -672,6 +684,7 @@ func TestRegistryLogin(t *testing.T) {
}

config.Doit.Set(config.NS, doctl.ArgRegistryExpirySeconds, test.expirySeconds)
config.Doit.Set(config.NS, doctl.ArgRegistryReadOnly, test.readOnly)

config.Out = os.Stderr
err := RunRegistryLogin(config)
Expand Down
44 changes: 42 additions & 2 deletions integration/registry_login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,16 @@ var _ = suite("registry/login", func(t *testing.T, when spec.G, it spec.S) {
return
}

readWriteParam := req.URL.Query().Get("read_write")
expiryParam := req.URL.Query().Get("expiry_seconds")
if expiryParam == "3600" {
w.Write([]byte(registryDockerCredentialsExpiryResponse))
} else if expiryParam == "" {
w.Write([]byte(registryDockerCredentialsResponse))
if readWriteParam == "false" {
w.Write([]byte(registryDockerCredentialsReadOnlyRegistryResponse))
} else {
w.Write([]byte(registryDockerCredentialsResponse))
}
} else {
t.Fatalf("received unknown value: %s", expiryParam)
}
Expand Down Expand Up @@ -131,8 +136,43 @@ var _ = suite("registry/login", func(t *testing.T, when spec.G, it spec.S) {
}
})
})

when("read-only flag is passed", func() {
it("add the correct query parameter", func() {
tmpDir := t.TempDir()

config := filepath.Join(tmpDir, "config.json")

cmd := exec.Command(builtBinaryPath,
"-t", "some-magic-token",
"-u", server.URL,
"registry",
"login",
"--read-only",
"true",
)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, fmt.Sprintf("DOCKER_CONFIG=%s", tmpDir))

output, err := cmd.CombinedOutput()
expect.NoError(err)

fileBytes, err := ioutil.ReadFile(config)
expect.NoError(err)

var dc dockerConfig
err = json.Unmarshal(fileBytes, &dc)
expect.NoError(err)

expect.Equal("Logging Docker in to registry.digitalocean.com\n", string(output))
for host := range dc.Auths {
expect.Equal("readonlyregistry.registry.com", host)
}
})
})
})

const (
registryDockerCredentialsExpiryResponse = `{"auths":{"expiring.registry.com":{"auth":"Y3JlZGVudGlhbHM6dGhhdGV4cGlyZQ=="}}}`
registryDockerCredentialsExpiryResponse = `{"auths":{"expiring.registry.com":{"auth":"Y3JlZGVudGlhbHM6dGhhdGV4cGlyZQ=="}}}`
registryDockerCredentialsReadOnlyRegistryResponse = `{"auths":{"readonlyregistry.registry.com":{"auth":"Y3JlZGVudGlhbHM6dGhhdGV4cGlyZQ=="}}}`
)