Skip to content

Commit 00d6aab

Browse files
committed
Implement authenticate & push
1 parent 5ba64e0 commit 00d6aab

3 files changed

Lines changed: 142 additions & 26 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package docker
2+
3+
import (
4+
"context"
5+
"encoding/base64"
6+
"encoding/json"
7+
"fmt"
8+
9+
"github.com/covexo/devspace/pkg/util/log"
10+
"github.com/docker/docker/api/types"
11+
"github.com/docker/docker/client"
12+
"github.com/docker/docker/registry"
13+
)
14+
15+
func getOfficialServer(ctx context.Context, client client.CommonAPIClient) string {
16+
// The daemon `/info` endpoint informs us of the default registry being
17+
// used. This is essential in cross-platforms environment, where for
18+
// example a Linux client might be interacting with a Windows daemon, hence
19+
// the default registry URL might be Windows specific.
20+
serverAddress := registry.IndexServer
21+
if info, err := client.Info(ctx); err != nil {
22+
// Only report the warning if we're in debug mode to prevent nagging during engine initialization workflows
23+
fmt.Fprintf(log.GetInstance(), "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress)
24+
} else if info.IndexServerAddress == "" {
25+
fmt.Fprintf(log.GetInstance(), "Warning: Empty registry endpoint from daemon. Using system default: %s\n", serverAddress)
26+
} else {
27+
serverAddress = info.IndexServerAddress
28+
}
29+
30+
return serverAddress
31+
}
32+
33+
func encodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
34+
buf, err := json.Marshal(authConfig)
35+
if err != nil {
36+
return "", err
37+
}
38+
return base64.URLEncoding.EncodeToString(buf), nil
39+
}
40+
41+
func getDefaultAuthConfig(client client.CommonAPIClient, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (*types.AuthConfig, error) {
42+
var authconfig types.AuthConfig
43+
var err error
44+
45+
configfile, _ := loadDockerConfig()
46+
47+
if !isDefaultRegistry {
48+
serverAddress = registry.ConvertToHostname(serverAddress)
49+
}
50+
51+
if checkCredStore {
52+
authconfig, err = configfile.GetAuthConfig(serverAddress)
53+
} else {
54+
authconfig = types.AuthConfig{}
55+
}
56+
57+
authconfig.ServerAddress = serverAddress
58+
authconfig.IdentityToken = ""
59+
return &authconfig, err
60+
}

pkg/devspace/builder/docker/client.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"k8s.io/client-go/tools/clientcmd"
1515
)
1616

17+
var isMinikubeVar *bool
18+
1719
func newDockerClientFromEnvironment() (client.CommonAPIClient, error) {
1820
cli, err := client.NewClientWithOpts(client.FromEnv)
1921
if err != nil {

pkg/devspace/builder/docker/docker.go

Lines changed: 80 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package docker
22

33
import (
44
"os"
5+
"strings"
56

67
"context"
78

9+
"github.com/docker/distribution/reference"
810
"github.com/docker/docker/pkg/term"
11+
"github.com/docker/docker/registry"
912

1013
"github.com/covexo/devspace/pkg/util/log"
1114
"github.com/docker/cli/cli/command/image/build"
@@ -21,16 +24,15 @@ import (
2124
"github.com/docker/docker/pkg/jsonmessage"
2225
)
2326

24-
var isMinikubeVar *bool
25-
2627
// Builder holds the necessary information to build and push docker images
2728
type Builder struct {
2829
RegistryURL string
2930
ImageName string
3031
ImageTag string
3132

32-
imageURL string
33-
client client.CommonAPIClient
33+
imageURL string
34+
authConfig *types.AuthConfig
35+
client client.CommonAPIClient
3436
}
3537

3638
// NewBuilder creates a new docker Builder instance
@@ -49,16 +51,34 @@ func NewBuilder(registryURL, imageName, imageTag string, preferMinikube bool) (*
4951
}
5052
}
5153

54+
// Check if it's the official registry or not
55+
ref, err := reference.ParseNormalizedNamed(registryURL)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
repoInfo, err := registry.ParseRepositoryInfo(ref)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
imageURL := registryURL + "/" + imageName + ":" + imageTag
66+
if repoInfo.Index.Official {
67+
imageURL = imageName + ":" + imageTag
68+
}
69+
5270
return &Builder{
5371
RegistryURL: registryURL,
5472
ImageName: imageName,
5573
ImageTag: imageTag,
56-
imageURL: registryURL + "/" + imageName + ":" + imageTag,
74+
imageURL: imageURL,
5775
client: cli,
5876
}, nil
5977
}
6078

6179
// BuildImage builds a dockerimage with the docker cli
80+
// contextPath is the absolute path to the context path
81+
// dockerfilePath is the absolute path to the dockerfile WITHIN the contextPath
6282
func (b *Builder) BuildImage(contextPath, dockerfilePath string, options *types.ImageBuildOptions) error {
6383
if options == nil {
6484
options = &types.ImageBuildOptions{}
@@ -76,7 +96,7 @@ func (b *Builder) BuildImage(contextPath, dockerfilePath string, options *types.
7696
}
7797

7898
if err := build.ValidateContextDirectory(contextDir, excludes); err != nil {
79-
return errors.Errorf("error checking context: '%s'.", err)
99+
return errors.Errorf("Error checking context: '%s'", err)
80100
}
81101

82102
// And canonicalize dockerfile name to a platform-independent one
@@ -105,7 +125,7 @@ func (b *Builder) BuildImage(contextPath, dockerfilePath string, options *types.
105125
AuthConfigs: authConfigs,
106126
})
107127
if err != nil {
108-
return errors.Wrap(err, "docker build")
128+
return err
109129
}
110130
defer response.Body.Close()
111131

@@ -118,36 +138,70 @@ func (b *Builder) BuildImage(contextPath, dockerfilePath string, options *types.
118138
return nil
119139
}
120140

121-
// Authenticate authenticates the cli with a remote registry
122-
func (b *Builder) Authenticate(user, password string) error {
123-
return nil
124-
}
141+
// Authenticate authenticates the client with a remote registry
142+
func (b *Builder) Authenticate(user, password string, checkCredentialsStore bool) error {
143+
ctx := context.Background()
144+
serverAddress := b.RegistryURL
145+
ref, err := reference.ParseNormalizedNamed(serverAddress)
146+
if err != nil {
147+
return err
148+
}
125149

126-
// PushImage pushes an image to the specified registry
127-
func (b *Builder) PushImage() error {
128-
/*if isMinikube() {
129-
err := pushImageMinikube(buildtag)
150+
repoInfo, err := registry.ParseRepositoryInfo(ref)
151+
if err != nil {
152+
return err
153+
}
130154

131-
if err == nil {
132-
return nil
133-
}
155+
authServer := getOfficialServer(ctx, b.client)
156+
if repoInfo.Index.Official {
157+
serverAddress = authServer
158+
}
134159

135-
// Fallback to normal docker cli if minikube failed
160+
authConfig, err := getDefaultAuthConfig(b.client, checkCredentialsStore, serverAddress, serverAddress == authServer)
161+
if err != nil || authConfig.Username == "" || authConfig.Password == "" {
162+
authConfig.Username = strings.TrimSpace(user)
163+
authConfig.Password = strings.TrimSpace(password)
136164
}
137165

138-
ctx := context.Background()
139-
dockerArgs := []string{"push", buildtag}
166+
response, err := b.client.RegistryLogin(ctx, *authConfig)
167+
if err != nil {
168+
return err
169+
}
140170

141-
cmd := exec.CommandContext(ctx, "docker", dockerArgs...)
171+
if response.IdentityToken != "" {
172+
authConfig.Password = ""
173+
authConfig.IdentityToken = response.IdentityToken
174+
}
142175

143-
cmd.Stdout = log.GetInstance()
144-
cmd.Stderr = log.GetInstance()
176+
b.authConfig = authConfig
177+
return nil
178+
}
145179

146-
err := cmd.Run()
180+
// PushImage pushes an image to the specified registry
181+
func (b *Builder) PushImage() error {
182+
ctx := context.Background()
183+
ref, err := reference.ParseNormalizedNamed(b.imageURL)
184+
if err != nil {
185+
return err
186+
}
147187

188+
encodedAuth, err := encodeAuthToBase64(*b.authConfig)
148189
if err != nil {
149190
return err
150-
}*/
191+
}
192+
193+
out, err := b.client.ImagePush(ctx, reference.FamiliarString(ref), types.ImagePushOptions{
194+
RegistryAuth: encodedAuth,
195+
})
196+
if err != nil {
197+
return err
198+
}
199+
200+
fd, _ := term.GetFdInfo(os.Stdout)
201+
err = jsonmessage.DisplayJSONMessagesStream(out, log.GetInstance(), fd, false, nil)
202+
if err != nil {
203+
return err
204+
}
151205

152206
return nil
153207
}

0 commit comments

Comments
 (0)