1 package image
2
3 import (
4 "fmt"
5 "os"
6 "os/exec"
7 "path/filepath"
8 "strings"
9
10 devfile "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
11 "github.com/fatih/color"
12 "k8s.io/klog"
13
14 dfutil "github.com/devfile/library/v2/pkg/util"
15
16 "github.com/redhat-developer/odo/pkg/log"
17 "github.com/redhat-developer/odo/pkg/testingutil/filesystem"
18 )
19
20
21 type DockerCompatibleBackend struct {
22 name string
23 globalExtraArgs []string
24 imageBuildExtraArgs []string
25 }
26
27 var _ Backend = (*DockerCompatibleBackend)(nil)
28
29 func NewDockerCompatibleBackend(name string, globalExtraArgs, imageBuildExtraArgs []string) *DockerCompatibleBackend {
30 return &DockerCompatibleBackend{
31 name: name,
32 globalExtraArgs: globalExtraArgs,
33 imageBuildExtraArgs: imageBuildExtraArgs,
34 }
35 }
36
37
38 func (o *DockerCompatibleBackend) Build(fs filesystem.Filesystem, image *devfile.ImageComponent, devfilePath string) error {
39
40 dockerfile, isTemp, err := resolveAndDownloadDockerfile(fs, image.Dockerfile.Uri)
41 if isTemp {
42 defer func(path string) {
43 if e := fs.Remove(path); e != nil {
44 klog.V(3).Infof("could not remove temporary Dockerfile at path %q: %v", path, err)
45 }
46 }(dockerfile)
47 }
48 if err != nil {
49 return err
50 }
51
52
53 buildSpinner := log.SpinnerNoSpin("Building image locally")
54 defer buildSpinner.End(false)
55
56 err = os.Setenv("PROJECTS_ROOT", devfilePath)
57 if err != nil {
58 return err
59 }
60
61 err = os.Setenv("PROJECT_SOURCE", devfilePath)
62 if err != nil {
63 return err
64 }
65
66 shellCmd := getShellCommand(o.name, o.globalExtraArgs, o.imageBuildExtraArgs, image, devfilePath, dockerfile)
67 klog.V(4).Infof("Running command: %v", shellCmd)
68 for i, cmd := range shellCmd {
69 shellCmd[i] = os.ExpandEnv(cmd)
70 }
71 cmd := exec.Command(shellCmd[0], shellCmd[1:]...)
72 cmdEnv := []string{
73 "PROJECTS_ROOT=" + devfilePath,
74 "PROJECT_SOURCE=" + devfilePath,
75 }
76 cmd.Env = append(os.Environ(), cmdEnv...)
77 cmd.Stdout = log.GetStdout()
78 cmd.Stderr = log.GetStderr()
79
80
81 color.Set(color.Italic)
82 defer color.Unset()
83 err = cmd.Run()
84 if err != nil {
85 return fmt.Errorf("error running %s command: %w", o.name, err)
86 }
87
88 buildSpinner.End(true)
89 return nil
90 }
91
92
93
94
95
96
97
98
99
100
101
102 func resolveAndDownloadDockerfile(fs filesystem.Filesystem, uri string) (string, bool, error) {
103 uriLower := strings.ToLower(uri)
104 if strings.HasPrefix(uriLower, "http://") || strings.HasPrefix(uriLower, "https://") {
105 s := log.Spinner("Downloading Dockerfile")
106 defer s.End(false)
107 tempFile, err := fs.TempFile("", "odo_*.dockerfile")
108 if err != nil {
109 return "", false, err
110 }
111 dockerfile := tempFile.Name()
112 err = dfutil.DownloadFile(dfutil.DownloadParams{
113 Request: dfutil.HTTPRequestParams{
114 URL: uri,
115 },
116 Filepath: dockerfile,
117 })
118 s.End(err == nil)
119 return dockerfile, true, err
120 }
121 return uri, false, nil
122 }
123
124
125
126 func getShellCommand(cmdName string, globalExtraArgs []string, buildExtraArgs []string, image *devfile.ImageComponent, devfilePath string, dockerfilePath string) []string {
127 imageName := image.ImageName
128 dockerfile := dockerfilePath
129 if !filepath.IsAbs(dockerfile) {
130 dockerfile = filepath.Join(devfilePath, dockerfilePath)
131 }
132 buildpath := image.Dockerfile.BuildContext
133 if buildpath == "" {
134 buildpath = devfilePath
135 }
136
137
138 shellCmd := make([]string, 0, len(globalExtraArgs)+len(buildExtraArgs)+len(image.Dockerfile.Args)+7)
139 shellCmd = append(shellCmd, cmdName)
140 shellCmd = append(shellCmd, globalExtraArgs...)
141 shellCmd = append(shellCmd, "build")
142 shellCmd = append(shellCmd, buildExtraArgs...)
143 shellCmd = append(shellCmd, "-t", imageName, "-f", dockerfile, buildpath)
144
145 if len(image.Dockerfile.Args) != 0 {
146 shellCmd = append(shellCmd, image.Dockerfile.Args...)
147 }
148 return shellCmd
149 }
150
151
152 func (o *DockerCompatibleBackend) Push(image string) error {
153
154
155 pushSpinner := log.SpinnerNoSpin("Pushing image to container registry")
156 defer pushSpinner.End(false)
157 klog.V(4).Infof("Running command: %s push %s", o.name, image)
158
159 cmd := exec.Command(o.name, "push", image)
160
161 cmd.Stdout = log.GetStdout()
162 cmd.Stderr = log.GetStderr()
163
164
165 color.Set(color.Italic)
166 defer color.Unset()
167 err := cmd.Run()
168 if err != nil {
169 return fmt.Errorf("error running %s command: %w", o.name, err)
170 }
171
172 pushSpinner.End(true)
173 return nil
174 }
175
176
177 func (o *DockerCompatibleBackend) String() string {
178 return o.name
179 }
180
View as plain text