Skip to content

Commit b421ce9

Browse files
authored
Merge pull request #188 from covexo/docker-builder
Docker builder
2 parents 4f08271 + efb8521 commit b421ce9

3 files changed

Lines changed: 148 additions & 25 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: 86 additions & 25 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,36 @@ func NewBuilder(registryURL, imageName, imageTag string, preferMinikube bool) (*
4951
}
5052
}
5153

54+
imageURL := imageName + ":" + imageTag
55+
if registryURL != "" {
56+
// Check if it's the official registry or not
57+
ref, err := reference.ParseNormalizedNamed(registryURL)
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
repoInfo, err := registry.ParseRepositoryInfo(ref)
63+
if err != nil {
64+
return nil, err
65+
}
66+
67+
if repoInfo.Index.Official == false {
68+
imageURL = registryURL + "/" + imageName + ":" + imageTag
69+
}
70+
}
71+
5272
return &Builder{
5373
RegistryURL: registryURL,
5474
ImageName: imageName,
5575
ImageTag: imageTag,
56-
imageURL: registryURL + "/" + imageName + ":" + imageTag,
76+
imageURL: imageURL,
5777
client: cli,
5878
}, nil
5979
}
6080

6181
// BuildImage builds a dockerimage with the docker cli
82+
// contextPath is the absolute path to the context path
83+
// dockerfilePath is the absolute path to the dockerfile WITHIN the contextPath
6284
func (b *Builder) BuildImage(contextPath, dockerfilePath string, options *types.ImageBuildOptions) error {
6385
if options == nil {
6486
options = &types.ImageBuildOptions{}
@@ -76,7 +98,7 @@ func (b *Builder) BuildImage(contextPath, dockerfilePath string, options *types.
7698
}
7799

78100
if err := build.ValidateContextDirectory(contextDir, excludes); err != nil {
79-
return errors.Errorf("error checking context: '%s'.", err)
101+
return errors.Errorf("Error checking context: '%s'", err)
80102
}
81103

82104
// And canonicalize dockerfile name to a platform-independent one
@@ -105,7 +127,7 @@ func (b *Builder) BuildImage(contextPath, dockerfilePath string, options *types.
105127
AuthConfigs: authConfigs,
106128
})
107129
if err != nil {
108-
return errors.Wrap(err, "docker build")
130+
return err
109131
}
110132
defer response.Body.Close()
111133

@@ -118,36 +140,75 @@ func (b *Builder) BuildImage(contextPath, dockerfilePath string, options *types.
118140
return nil
119141
}
120142

121-
// Authenticate authenticates the cli with a remote registry
122-
func (b *Builder) Authenticate(user, password string) error {
123-
return nil
124-
}
143+
// Authenticate authenticates the client with a remote registry
144+
func (b *Builder) Authenticate(user, password string, checkCredentialsStore bool) error {
145+
ctx := context.Background()
146+
authServer := getOfficialServer(ctx, b.client)
147+
serverAddress := b.RegistryURL
125148

126-
// PushImage pushes an image to the specified registry
127-
func (b *Builder) PushImage() error {
128-
/*if isMinikube() {
129-
err := pushImageMinikube(buildtag)
149+
if serverAddress == "" {
150+
serverAddress = authServer
151+
} else {
152+
ref, err := reference.ParseNormalizedNamed(serverAddress)
153+
if err != nil {
154+
return err
155+
}
130156

131-
if err == nil {
132-
return nil
157+
repoInfo, err := registry.ParseRepositoryInfo(ref)
158+
if err != nil {
159+
return err
133160
}
134161

135-
// Fallback to normal docker cli if minikube failed
162+
if repoInfo.Index.Official {
163+
serverAddress = authServer
164+
}
136165
}
137166

138-
ctx := context.Background()
139-
dockerArgs := []string{"push", buildtag}
167+
authConfig, err := getDefaultAuthConfig(b.client, checkCredentialsStore, serverAddress, serverAddress == authServer)
168+
if err != nil || authConfig.Username == "" || authConfig.Password == "" {
169+
authConfig.Username = strings.TrimSpace(user)
170+
authConfig.Password = strings.TrimSpace(password)
171+
}
140172

141-
cmd := exec.CommandContext(ctx, "docker", dockerArgs...)
173+
response, err := b.client.RegistryLogin(ctx, *authConfig)
174+
if err != nil {
175+
return err
176+
}
177+
178+
if response.IdentityToken != "" {
179+
authConfig.Password = ""
180+
authConfig.IdentityToken = response.IdentityToken
181+
}
182+
183+
b.authConfig = authConfig
184+
return nil
185+
}
186+
187+
// PushImage pushes an image to the specified registry
188+
func (b *Builder) PushImage() error {
189+
ctx := context.Background()
190+
ref, err := reference.ParseNormalizedNamed(b.imageURL)
191+
if err != nil {
192+
return err
193+
}
142194

143-
cmd.Stdout = log.GetInstance()
144-
cmd.Stderr = log.GetInstance()
195+
encodedAuth, err := encodeAuthToBase64(*b.authConfig)
196+
if err != nil {
197+
return err
198+
}
145199

146-
err := cmd.Run()
200+
out, err := b.client.ImagePush(ctx, reference.FamiliarString(ref), types.ImagePushOptions{
201+
RegistryAuth: encodedAuth,
202+
})
203+
if err != nil {
204+
return err
205+
}
147206

207+
fd, _ := term.GetFdInfo(os.Stdout)
208+
err = jsonmessage.DisplayJSONMessagesStream(out, log.GetInstance(), fd, false, nil)
148209
if err != nil {
149210
return err
150-
}*/
211+
}
151212

152213
return nil
153214
}

0 commit comments

Comments
 (0)