forked from aman-harness/drone-kaniko
-
Notifications
You must be signed in to change notification settings - Fork 0
/
kaniko.go
243 lines (211 loc) · 8.23 KB
/
kaniko.go
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
package kaniko
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/drone/drone-kaniko/pkg/artifact"
"github.com/drone/drone-kaniko/pkg/tagger"
"golang.org/x/mod/semver"
)
type (
// Build defines Docker build parameters.
Build struct {
DroneCommitRef string // Drone git commit reference
DroneRepoBranch string // Drone repo branch
Dockerfile string // Docker build Dockerfile
Context string // Docker build context
Tags []string // Docker build tags
AutoTag bool // Set this to auto detect tags from git commits and semver-tagged labels
AutoTagSuffix string // Suffix to append to the auto detect tags
ExpandTag bool // Set this to expand the `Tags` into semver-tagged labels
Args []string // Docker build args
Target string // Docker build target
Repo string // Docker build repository
Mirrors []string // Docker repository mirrors
Labels []string // Label map
SkipTlsVerify bool // Docker skip tls certificate verify for registry
SnapshotMode string // Kaniko snapshot mode
EnableCache bool // Whether to enable kaniko cache
CacheRepo string // Remote repository that will be used to store cached layers
CacheTTL int // Cache timeout in hours
DigestFile string // Digest file location
NoPush bool // Set this flag if you only want to build the image, without pushing to a registry
Verbosity string // Log level
Platform string // Allows to build with another default platform than the host, similarly to docker build --platform
SkipUnusedStages bool // Build only used stages
}
// Artifact defines content of artifact file
Artifact struct {
Tags []string // Docker artifact tags
Repo string // Docker artifact repository
Registry string // Docker artifact registry
RegistryType artifact.RegistryTypeEnum // Rocker artifact registry type
ArtifactFile string // Artifact file location
}
// Plugin defines the Docker plugin parameters.
Plugin struct {
Build Build // Docker build configuration
Artifact Artifact // Artifact file content
}
)
// labelsForTag returns the labels to use for the given tag, subject to the value of ExpandTag.
//
// Build information (e.g. +linux_amd64) is carried through to all labels.
// Pre-release information (e.g. -rc1) suppresses major and major+minor auto-labels.
func (b Build) labelsForTag(tag string) (labels []string) {
// We strip "v" off of the beginning of semantic versions, as they are not used in docker tags
const VersionPrefix = "v"
// Semantic Versions don't allow underscores, so replace them with dashes.
// https://semver.org/
semverTag := strings.ReplaceAll(tag, "_", "-")
// Allow tags of the form "1.2.3" as well as "v1.2.3" to avoid confusion.
if withV := VersionPrefix + semverTag; !semver.IsValid(semverTag) && semver.IsValid(withV) {
semverTag = withV
}
// Pass through tags if expand-tag is not set, or if the tag is not a semantic version
if !b.ExpandTag || !semver.IsValid(semverTag) {
return []string{tag}
}
tag = semverTag
// If the version is pre-release, only the full release should be tagged, not the major/minor versions.
if semver.Prerelease(tag) != "" {
return []string{
strings.TrimPrefix(tag, VersionPrefix),
}
}
// tagFor carries any build information from the semantic version through to major and minor tags.
labelFor := func(base string) string {
return strings.TrimPrefix(base, VersionPrefix) + semver.Build(tag)
}
return []string{
labelFor(semver.Major(tag)),
labelFor(semver.MajorMinor(tag)),
labelFor(semver.Canonical(tag)),
}
}
// Returns the auto detected tags. See the AutoTag section of
// https://plugins.drone.io/drone-plugins/drone-docker/ for more info.
func (b Build) AutoTags() (tags []string, err error) {
if len(b.Tags) > 1 || len(b.Tags) == 1 && b.Tags[0] != "latest" {
err = fmt.Errorf("The auto-tag flag does not work with user provided tags %s", b.Tags)
return
}
// We have tried the best to prevent enabling auto-tag and passing in
// user specified at the same time. Starts to auto detect tags.
// Note: passing in a "latest" tag with auto-tag enabled won't trigger the
// early returns above, because we cannot tell if the tag is provided by
// the default value or by the users.
commitRef := b.DroneCommitRef
if !tagger.UseAutoTag(commitRef, b.DroneRepoBranch) {
err = fmt.Errorf("Could not auto detect the tag. Skipping automated docker build for commit %s", commitRef)
return
}
tags, err = tagger.AutoTagsSuffix(commitRef, b.AutoTagSuffix)
if err != nil {
err = fmt.Errorf("Invalid semantic version when auto detecting the tag. Skipping automated docker build for %s.", commitRef)
}
return
}
// Exec executes the plugin step
func (p Plugin) Exec() error {
if !p.Build.NoPush && p.Build.Repo == "" {
return fmt.Errorf("repository name to publish image must be specified")
}
if _, err := os.Stat(p.Build.Dockerfile); os.IsNotExist(err) {
return fmt.Errorf("dockerfile does not exist at path: %s", p.Build.Dockerfile)
}
var tags = p.Build.Tags
if p.Build.AutoTag && p.Build.ExpandTag {
return fmt.Errorf("The auto-tag flag conflicts with the expand-tag flag")
}
if p.Build.AutoTag {
var err error
tags, err = p.Build.AutoTags()
if err != nil {
return err
}
}
cmdArgs := []string{
fmt.Sprintf("--dockerfile=%s", p.Build.Dockerfile),
fmt.Sprintf("--context=dir://%s", p.Build.Context),
}
// Set the destination repository
if !p.Build.NoPush {
for _, tag := range tags {
for _, label := range p.Build.labelsForTag(tag) {
cmdArgs = append(cmdArgs, fmt.Sprintf("--destination=%s:%s", p.Build.Repo, label))
}
}
}
// Set the build arguments
for _, arg := range p.Build.Args {
cmdArgs = append(cmdArgs, fmt.Sprintf("--build-arg=%s", arg))
}
// Set the labels
for _, label := range p.Build.Labels {
cmdArgs = append(cmdArgs, fmt.Sprintf("--label=%s", label))
}
// Set repository mirrors
for _, mirror := range p.Build.Mirrors {
cmdArgs = append(cmdArgs, fmt.Sprintf("--registry-mirror=%s", mirror))
}
if p.Build.Target != "" {
cmdArgs = append(cmdArgs, fmt.Sprintf("--target=%s", p.Build.Target))
}
if p.Build.SkipTlsVerify {
cmdArgs = append(cmdArgs, "--skip-tls-verify=true")
}
if p.Build.SnapshotMode != "" {
cmdArgs = append(cmdArgs, fmt.Sprintf("--snapshotMode=%s", p.Build.SnapshotMode))
}
if p.Build.EnableCache {
cmdArgs = append(cmdArgs, "--cache=true")
if p.Build.CacheRepo != "" {
cmdArgs = append(cmdArgs, fmt.Sprintf("--cache-repo=%s", p.Build.CacheRepo))
}
}
if p.Build.CacheTTL != 0 {
cmdArgs = append(cmdArgs, fmt.Sprintf("--cache-ttl=%dh", p.Build.CacheTTL))
}
if p.Build.DigestFile != "" {
cmdArgs = append(cmdArgs, fmt.Sprintf("--digest-file=%s", p.Build.DigestFile))
}
if p.Build.NoPush {
cmdArgs = append(cmdArgs, "--no-push")
}
if p.Build.Verbosity != "" {
cmdArgs = append(cmdArgs, fmt.Sprintf("--verbosity=%s", p.Build.Verbosity))
}
if p.Build.Platform != "" {
cmdArgs = append(cmdArgs, fmt.Sprintf("--customPlatform=%s", p.Build.Platform))
}
if p.Build.SkipUnusedStages {
cmdArgs = append(cmdArgs, "--skip-unused-stages")
}
cmd := exec.Command("/kaniko/executor", cmdArgs...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
trace(cmd)
err := cmd.Run()
if err != nil {
return err
}
if p.Build.DigestFile != "" && p.Artifact.ArtifactFile != "" {
content, err := ioutil.ReadFile(p.Build.DigestFile)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to read digest file contents at path: %s with error: %s\n", p.Build.DigestFile, err)
}
err = artifact.WritePluginArtifactFile(p.Artifact.RegistryType, p.Artifact.ArtifactFile, p.Artifact.Registry, p.Artifact.Repo, string(content), p.Artifact.Tags)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to write plugin artifact file at path: %s with error: %s\n", p.Artifact.ArtifactFile, err)
}
}
return nil
}
// trace writes each command to stdout with the command wrapped in an xml
// tag so that it can be extracted and displayed in the logs.
func trace(cmd *exec.Cmd) {
fmt.Fprintf(os.Stdout, "+ %s\n", strings.Join(cmd.Args, " "))
}