1
15
16 package docker
17
18 import (
19 "context"
20 "fmt"
21 "io/ioutil"
22 "net/http"
23 "os"
24 "path/filepath"
25 "testing"
26 "time"
27
28 "github.com/distribution/distribution/v3/configuration"
29 "github.com/distribution/distribution/v3/registry"
30 _ "github.com/distribution/distribution/v3/registry/auth/htpasswd"
31 _ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
32 "github.com/phayes/freeport"
33 "github.com/stretchr/testify/suite"
34 "golang.org/x/crypto/bcrypt"
35 iface "oras.land/oras-go/pkg/auth"
36 )
37
38 var (
39 testConfig = "test.config"
40 testHtpasswd = "test.htpasswd"
41 testUsername = "alice"
42 testPassword = "wonderland"
43 )
44
45 type DockerClientTestSuite struct {
46 suite.Suite
47 DockerRegistryHost string
48 Client *Client
49 TempTestDir string
50 }
51
52 func newContext() context.Context {
53 return context.Background()
54 }
55
56 func (suite *DockerClientTestSuite) SetupSuite() {
57 tempDir, err := ioutil.TempDir("", "oras_auth_docker_test")
58 suite.Nil(err, "no error creating temp directory for test")
59 suite.TempTestDir = tempDir
60
61
62 client, err := NewClient(filepath.Join(suite.TempTestDir, testConfig))
63 suite.Nil(err, "no error creating client")
64 var ok bool
65 suite.Client, ok = client.(*Client)
66 suite.True(ok, "NewClient returns a *docker.Client inside")
67
68
69 secret, err := bcrypt.GenerateFromPassword([]byte(testPassword), bcrypt.DefaultCost)
70 suite.Nil(err, "no error generating bcrypt password for test htpasswd file")
71 authRecord := fmt.Sprintf("%s:%s\n", testUsername, string(secret))
72 htpasswdPath := filepath.Join(suite.TempTestDir, testHtpasswd)
73 err = ioutil.WriteFile(htpasswdPath, []byte(authRecord), 0644)
74 suite.Nil(err, "no error creating test htpasswd file")
75
76
77 config := &configuration.Configuration{}
78 port, err := freeport.GetFreePort()
79 suite.Nil(err, "no error finding free port for test registry")
80 suite.DockerRegistryHost = fmt.Sprintf("localhost:%d", port)
81 config.HTTP.Addr = fmt.Sprintf(":%d", port)
82 config.HTTP.DrainTimeout = time.Duration(10) * time.Second
83 config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}}
84 config.Auth = configuration.Auth{
85 "htpasswd": configuration.Parameters{
86 "realm": "localhost",
87 "path": htpasswdPath,
88 },
89 }
90 dockerRegistry, err := registry.NewRegistry(context.Background(), config)
91 suite.Nil(err, "no error finding free port for test registry")
92
93
94 go dockerRegistry.ListenAndServe()
95 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
96 defer cancel()
97 for {
98 select {
99 case <-ctx.Done():
100 suite.FailNow("docker registry timed out")
101 default:
102 }
103 req, err := http.NewRequestWithContext(
104 ctx,
105 http.MethodGet,
106 fmt.Sprintf("http://%s/v2/", suite.DockerRegistryHost),
107 nil,
108 )
109 suite.Nil(err, "no error in generate a /v2/ request")
110 resp, err := http.DefaultClient.Do(req)
111 if err == nil {
112 resp.Body.Close()
113 break
114 }
115 time.Sleep(time.Second)
116 }
117 }
118
119 func (suite *DockerClientTestSuite) TearDownSuite() {
120 os.RemoveAll(suite.TempTestDir)
121 }
122
123 func (suite *DockerClientTestSuite) Test_0_Login() {
124 var err error
125
126 err = suite.Client.Login(newContext(), suite.DockerRegistryHost, "oscar", "opponent", false)
127 suite.NotNil(err, "error logging into registry with invalid credentials")
128
129 err = suite.Client.Login(newContext(), suite.DockerRegistryHost, testUsername, testPassword, false)
130 suite.Nil(err, "no error logging into registry with valid credentials")
131 }
132
133 func (suite *DockerClientTestSuite) Test_1_LoginWithOpts() {
134 var err error
135
136 opts := []iface.LoginOption{
137 iface.WithLoginContext(newContext()),
138 iface.WithLoginHostname(suite.DockerRegistryHost),
139 iface.WithLoginUsername("oscar"),
140 iface.WithLoginSecret("opponent"),
141 }
142 err = suite.Client.LoginWithOpts(opts...)
143 suite.NotNil(err, "error logging into registry with invalid credentials (LoginWithOpts)")
144
145 opts = []iface.LoginOption{
146 iface.WithLoginContext(newContext()),
147 iface.WithLoginHostname(suite.DockerRegistryHost),
148 iface.WithLoginUsername(testUsername),
149 iface.WithLoginSecret(testPassword),
150 }
151 err = suite.Client.LoginWithOpts(opts...)
152 suite.Nil(err, "no error logging into registry with valid credentials (LoginWithOpts)")
153 }
154
155 func (suite *DockerClientTestSuite) Test_2_Logout() {
156 var err error
157
158 err = suite.Client.Logout(newContext(), "non-existing-host:42")
159 suite.NotNil(err, "error logging out of registry that has no entry")
160
161 err = suite.Client.Logout(newContext(), suite.DockerRegistryHost)
162 suite.Nil(err, "no error logging out of registry")
163 }
164
165 func TestDockerClientTestSuite(t *testing.T) {
166 suite.Run(t, new(DockerClientTestSuite))
167 }
168
View as plain text