This repository was archived by the owner on Apr 1, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 244
Expand file tree
/
Copy pathdocker_compatible.go
More file actions
179 lines (153 loc) · 5.57 KB
/
docker_compatible.go
File metadata and controls
179 lines (153 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package image
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
devfile "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/fatih/color"
"k8s.io/klog/v2"
dfutil "github.com/devfile/library/v2/pkg/util"
"github.com/redhat-developer/odo/pkg/log"
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
)
// DockerCompatibleBackend uses a CLI compatible with the docker CLI (at least docker itself and podman)
type DockerCompatibleBackend struct {
name string
globalExtraArgs []string
imageBuildExtraArgs []string
}
var _ Backend = (*DockerCompatibleBackend)(nil)
func NewDockerCompatibleBackend(name string, globalExtraArgs, imageBuildExtraArgs []string) *DockerCompatibleBackend {
return &DockerCompatibleBackend{
name: name,
globalExtraArgs: globalExtraArgs,
imageBuildExtraArgs: imageBuildExtraArgs,
}
}
// Build an image, as defined in devfile, using a Docker compatible CLI
func (o *DockerCompatibleBackend) Build(fs filesystem.Filesystem, image *devfile.ImageComponent, devfilePath string) error {
dockerfile, isTemp, err := resolveAndDownloadDockerfile(fs, image.Dockerfile.Uri)
if isTemp {
defer func(path string) {
if e := fs.Remove(path); e != nil {
klog.V(3).Infof("could not remove temporary Dockerfile at path %q: %v", path, err)
}
}(dockerfile)
}
if err != nil {
return err
}
// We use a "No Spin" since we are outputting to stdout / stderr
buildSpinner := log.SpinnerNoSpin("Building image locally")
defer buildSpinner.End(false)
err = os.Setenv("PROJECTS_ROOT", devfilePath)
if err != nil {
return err
}
err = os.Setenv("PROJECT_SOURCE", devfilePath)
if err != nil {
return err
}
shellCmd := getShellCommand(o.name, o.globalExtraArgs, o.imageBuildExtraArgs, image, devfilePath, dockerfile)
klog.V(4).Infof("Running command: %v", shellCmd)
for i, cmd := range shellCmd {
shellCmd[i] = os.ExpandEnv(cmd)
}
cmd := exec.Command(shellCmd[0], shellCmd[1:]...)
cmdEnv := []string{
"PROJECTS_ROOT=" + devfilePath,
"PROJECT_SOURCE=" + devfilePath,
}
cmd.Env = append(os.Environ(), cmdEnv...)
cmd.Stdout = log.GetStdout()
cmd.Stderr = log.GetStderr()
// Set all output as italic when doing a push, then return to normal at the end
color.Set(color.Italic)
defer color.Unset()
err = cmd.Run()
if err != nil {
return fmt.Errorf("error running %s command: %w", o.name, err)
}
buildSpinner.End(true)
return nil
}
// resolveAndDownloadDockerfile resolves and downloads (if needed) the specified Dockerfile URI.
// For now, it only supports resolving HTTP(S) URIs, in which case it downloads the remote file
// to a temporary file. The path to that temporary file is then returned.
//
// In all other cases, the specified URI path is returned as is.
// This means that non-HTTP(S) URIs will *not* get resolved, but will be returned as is.
//
// In addition to the path, a boolean and a potential error are returned. The boolean indicates whether
// the returned path is a temporary one; in such case, it is the caller's responsibility to delete this file
// once it is done working with it.
func resolveAndDownloadDockerfile(fs filesystem.Filesystem, uri string) (string, bool, error) {
uriLower := strings.ToLower(uri)
if strings.HasPrefix(uriLower, "http://") || strings.HasPrefix(uriLower, "https://") {
s := log.Spinner("Downloading Dockerfile")
defer s.End(false)
tempFile, err := fs.TempFile("", "odo_*.dockerfile")
if err != nil {
return "", false, err
}
dockerfile := tempFile.Name()
err = dfutil.DownloadFile(dfutil.DownloadParams{
Request: dfutil.HTTPRequestParams{
URL: uri,
},
Filepath: dockerfile,
})
s.End(err == nil)
return dockerfile, true, err
}
return uri, false, nil
}
// getShellCommand creates the docker compatible build command from detected backend,
// container image and devfile path
func getShellCommand(cmdName string, globalExtraArgs []string, buildExtraArgs []string, image *devfile.ImageComponent, devfilePath string, dockerfilePath string) []string {
imageName := image.ImageName
dockerfile := dockerfilePath
if !filepath.IsAbs(dockerfile) {
dockerfile = filepath.Join(devfilePath, dockerfilePath)
}
buildpath := image.Dockerfile.BuildContext
if buildpath == "" {
buildpath = devfilePath
}
// +7 because of the other args
shellCmd := make([]string, 0, len(globalExtraArgs)+len(buildExtraArgs)+len(image.Dockerfile.Args)+7)
shellCmd = append(shellCmd, cmdName)
shellCmd = append(shellCmd, globalExtraArgs...)
shellCmd = append(shellCmd, "build")
shellCmd = append(shellCmd, buildExtraArgs...)
shellCmd = append(shellCmd, "-t", imageName, "-f", dockerfile, buildpath)
if len(image.Dockerfile.Args) != 0 {
shellCmd = append(shellCmd, image.Dockerfile.Args...)
}
return shellCmd
}
// Push an image to its registry using a Docker compatible CLI
func (o *DockerCompatibleBackend) Push(image string) error {
// We use a "No Spin" since we are outputting to stdout / stderr
pushSpinner := log.SpinnerNoSpin("Pushing image to container registry")
defer pushSpinner.End(false)
klog.V(4).Infof("Running command: %s push %s", o.name, image)
cmd := exec.Command(o.name, "push", image)
cmd.Stdout = log.GetStdout()
cmd.Stderr = log.GetStderr()
// Set all output as italic when doing a push, then return to normal at the end
color.Set(color.Italic)
defer color.Unset()
err := cmd.Run()
if err != nil {
return fmt.Errorf("error running %s command: %w", o.name, err)
}
pushSpinner.End(true)
return nil
}
// String return the name of the docker compatible CLI used
func (o *DockerCompatibleBackend) String() string {
return o.name
}