Skip to content

Commit 45401d5

Browse files
committed
Add docker plugin PoC
1 parent d36189b commit 45401d5

File tree

6 files changed

+221
-0
lines changed

6 files changed

+221
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/bash
2+
3+
# Credentials helper that returns values from environment variables:
4+
# * DOCKER_CREDS_USERNAME: the username
5+
# * DOCKER_CREDS_PASSWORD: the password / token
6+
# https://docs.docker.com/engine/reference/commandline/login/
7+
die() {
8+
echo "$@" 1>&2
9+
exit 1
10+
}
11+
12+
case "$1" in
13+
get)
14+
read -r HOST
15+
if [ "$HOST" = "$REG" ]; then
16+
printf '{"ServerURL":"%s","Username":"%q","Secret":"%q"}\n' \
17+
"$HOST" "$DOCKER_CREDS_USR" "$DOCKER_CREDS_PSW"
18+
else
19+
die "No credentials available for $HOST"
20+
fi
21+
;;
22+
*)
23+
die "Unsupported operation"
24+
;;
25+
esac

plugins/docker/docker.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package docker
2+
3+
import (
4+
"github.com/1Password/shell-plugins/sdk"
5+
"github.com/1Password/shell-plugins/sdk/needsauth"
6+
"github.com/1Password/shell-plugins/sdk/schema"
7+
)
8+
9+
func DockerCLI() schema.Executable {
10+
return schema.Executable{
11+
Name: "Docker CLI", // TODO: Check if this is correct
12+
Runs: []string{"docker"},
13+
DocsURL: sdk.URL("https://docker.com/docs/cli"), // TODO: Replace with actual URL
14+
NeedsAuth: needsauth.IfAll(
15+
needsauth.NotForHelpOrVersion(),
16+
needsauth.NotWithoutArgs(),
17+
),
18+
Uses: []schema.CredentialUsage{
19+
{
20+
Name: "credname.UserLogin",
21+
},
22+
},
23+
}
24+
}

plugins/docker/plugin.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package docker
2+
3+
import (
4+
"github.com/1Password/shell-plugins/sdk"
5+
"github.com/1Password/shell-plugins/sdk/schema"
6+
)
7+
8+
func New() schema.Plugin {
9+
return schema.Plugin{
10+
Name: "docker",
11+
Platform: schema.PlatformInfo{
12+
Name: "Docker",
13+
Homepage: sdk.URL("https://docker.com"), // TODO: Check if this is correct
14+
},
15+
Credentials: []schema.CredentialType{
16+
UserCredentials(),
17+
},
18+
Executables: []schema.Executable{
19+
DockerCLI(),
20+
},
21+
}
22+
}

plugins/docker/provisioner.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package docker
2+
3+
import (
4+
"context"
5+
"github.com/1Password/shell-plugins/sdk"
6+
)
7+
8+
type DockerProvisioner struct {
9+
}
10+
11+
func dockerConfig(in sdk.ProvisionInput) ([]byte, error) {
12+
return []byte(`{
13+
"auths": {
14+
"https://index.docker.io/v1/": {
15+
"credHelper": "envvars"
16+
}
17+
},
18+
"currentContext": "desktop-linux"
19+
}`), nil
20+
}
21+
22+
func (d DockerProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) {
23+
// Nothing to do here: environment variables get wiped automatically when the process exits.
24+
}
25+
26+
func (d DockerProvisioner) Description() string {
27+
return "Provision environment variables with temporary STS credentials AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN"
28+
}

plugins/docker/user_credentials.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package docker
2+
3+
import (
4+
"context"
5+
"github.com/1Password/shell-plugins/sdk"
6+
"github.com/1Password/shell-plugins/sdk/importer"
7+
"github.com/1Password/shell-plugins/sdk/provision"
8+
"github.com/1Password/shell-plugins/sdk/schema"
9+
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
10+
)
11+
12+
func UserCredentials() schema.CredentialType {
13+
return schema.CredentialType{
14+
Name: "", // TODO: Register name in project://sdk/schema/credname/names.go
15+
DocsURL: sdk.URL("https://docker.com/docs/user_credentials"), // TODO: Replace with actual URL
16+
ManagementURL: sdk.URL("https://console.docker.com/user/security/tokens"), // TODO: Replace with actual URL
17+
Fields: []schema.CredentialField{
18+
{
19+
Name: fieldname.Username,
20+
MarkdownDescription: " used to authenticate to Docker.",
21+
},
22+
{
23+
Name: fieldname.Password,
24+
MarkdownDescription: " used to authenticate to Docker.",
25+
Secret: true,
26+
},
27+
},
28+
DefaultProvisioner: provision.TempFile(dockerConfig, provision.AtFixedPath("~/.docker/config.json")),
29+
Importer: importer.TryAll(
30+
TryDockerConfigFile(),
31+
)}
32+
}
33+
34+
type PerHostAuth struct {
35+
UsernamePassword string `json:"auth"`
36+
}
37+
38+
type Config struct {
39+
Auths map[string]PerHostAuth `json:"auths"`
40+
}
41+
42+
// TODO: Check if the platform stores the User Credentials in a local config file, and if so,
43+
// implement the function below to add support for importing it.
44+
func TryDockerConfigFile() sdk.Importer {
45+
return importer.TryFile("~/path/to/config/file.yml", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) {
46+
// var config Config
47+
// if err := contents.ToYAML(&config); err != nil {
48+
// out.AddError(err)
49+
// return
50+
// }
51+
52+
// if config. == "" {
53+
// return
54+
// }
55+
56+
// out.AddCandidate(sdk.ImportCandidate{
57+
// Fields: map[sdk.FieldName]string{
58+
// fieldname.: config.,
59+
// },
60+
// })
61+
})
62+
}
63+
64+
// TODO: Implement the config file schema
65+
// type Config struct {
66+
// string
67+
// }
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package docker
2+
3+
import (
4+
"testing"
5+
6+
"github.com/1Password/shell-plugins/sdk"
7+
"github.com/1Password/shell-plugins/sdk/plugintest"
8+
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
9+
)
10+
11+
func TestUserCredentialsProvisioner(t *testing.T) {
12+
plugintest.TestProvisioner(t, UserCredentials().DefaultProvisioner, map[string]plugintest.ProvisionCase{
13+
"default": {
14+
ItemFields: map[sdk.FieldName]string{ // TODO: Check if this is correct
15+
fieldname.: "7nJeRV69EYA3zvEjpSmwb5GEXAMPLE",
16+
},
17+
ExpectedOutput: sdk.ProvisionOutput{
18+
Environment: map[string]string{
19+
"DOCKER": "7nJeRV69EYA3zvEjpSmwb5GEXAMPLE",
20+
},
21+
},
22+
},
23+
})
24+
}
25+
26+
func TestUserCredentialsImporter(t *testing.T) {
27+
plugintest.TestImporter(t, UserCredentials().Importer, map[string]plugintest.ImportCase{
28+
"environment": {
29+
Environment: map[string]string{ // TODO: Check if this is correct
30+
"DOCKER": "7nJeRV69EYA3zvEjpSmwb5GEXAMPLE",
31+
},
32+
ExpectedCandidates: []sdk.ImportCandidate{
33+
{
34+
Fields: map[sdk.FieldName]string{
35+
fieldname.: "7nJeRV69EYA3zvEjpSmwb5GEXAMPLE",
36+
},
37+
},
38+
},
39+
},
40+
// TODO: If you implemented a config file importer, add a test file example in docker/test-fixtures
41+
// and fill the necessary details in the test template below.
42+
"config file": {
43+
Files: map[string]string{
44+
// "~/path/to/config.yml": plugintest.LoadFixture(t, "config.yml"),
45+
},
46+
ExpectedCandidates: []sdk.ImportCandidate{
47+
// {
48+
// Fields: map[sdk.FieldName]string{
49+
// fieldname.Token: "7nJeRV69EYA3zvEjpSmwb5GEXAMPLE",
50+
// },
51+
// },
52+
},
53+
},
54+
})
55+
}

0 commit comments

Comments
 (0)