Skip to content

Commit 699c6ed

Browse files
Divyabillyb2
andauthored
Add label to images (#2814)
* feat: add label flag toflyctl * feat: Add labels to image * fix: Update description * wip: brute force test * feat: extract gh values * fix: Move env look up to env package * Show labels in `image show` * Properly test for null json value * Add a test --------- Co-authored-by: William Batista <[email protected]>
1 parent f0325f2 commit 699c6ed

File tree

7 files changed

+106
-3
lines changed

7 files changed

+106
-3
lines changed

internal/build/imgsrc/dockerfile_builder.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ func runClassicBuild(ctx context.Context, streams *iostreams.IOStreams, docker *
290290
Dockerfile: dockerfilePath,
291291
Target: opts.Target,
292292
NoCache: opts.NoCache,
293+
Labels: opts.Label,
293294
}
294295

295296
resp, err := docker.ImageBuild(ctx, r, options)
@@ -326,6 +327,10 @@ func solveOptFromImageOptions(opts ImageOptions, dockerfilePath string, buildArg
326327
attrs["no-cache"] = ""
327328
}
328329

330+
for k, v := range opts.Label {
331+
attrs["label:"+k] = v
332+
}
333+
329334
for k, v := range buildArgs {
330335
if v == nil {
331336
continue

internal/build/imgsrc/resolver.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type ImageOptions struct {
4343
BuiltInSettings map[string]interface{}
4444
Builder string
4545
Buildpacks []string
46+
Label map[string]string
4647
}
4748

4849
type RefOptions struct {
@@ -55,9 +56,10 @@ type RefOptions struct {
5556
}
5657

5758
type DeploymentImage struct {
58-
ID string
59-
Tag string
60-
Size int64
59+
ID string
60+
Tag string
61+
Size int64
62+
Labels map[string]string
6163
}
6264

6365
type Resolver struct {

internal/command/deploy/deploy.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ var CommonFlags = flag.Set{
127127
Name: "only-regions",
128128
Description: "Deploy to machines only in these regions. Multiple regions can be specified with comma separated values or by providing the flag multiple times. --only-regions iad,sea --only-regions syd will deploy to all three iad, sea, and syd regions. Applied before --exclude-regions. V2 machines platform only.",
129129
},
130+
flag.StringArray{
131+
Name: "label",
132+
Description: "Add custom metadata to an image via docker labels",
133+
},
130134
flag.VMSizeFlags,
131135
}
132136

internal/command/deploy/deploy_build.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,19 @@ func determineImage(ctx context.Context, appConfig *appconfig.Config) (img *imgs
108108
opts.BuildSecrets = cliBuildSecrets
109109
}
110110

111+
arrLabels := flag.GetStringArray(ctx, "label")
112+
labels, err := cmdutil.ParseKVStringsToMap(arrLabels)
113+
if err != nil {
114+
return
115+
}
116+
if env.IS_GH_ACTION() {
117+
labels["GH_SHA"] = env.GitCommitSHA()
118+
labels["GH_EVENT_NAME"] = env.GitActionEventName()
119+
}
120+
if labels != nil {
121+
opts.Label = labels
122+
}
123+
111124
var buildArgs map[string]string
112125
if buildArgs, err = mergeBuildArgs(ctx, build.Args); err != nil {
113126
return

internal/command/image/show.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package image
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"strings"
78

@@ -167,13 +168,30 @@ func showMachineImage(ctx context.Context, app *api.AppCompact) error {
167168
version = machine.ImageVersion()
168169
}
169170

171+
var labelsString string
172+
173+
if cfg.JSONOutput {
174+
json, err := json.Marshal(machine.ImageRef.Labels)
175+
if err != nil {
176+
return err
177+
}
178+
if string(json) != "null" {
179+
labelsString = string(json)
180+
}
181+
} else {
182+
for key, val := range machine.ImageRef.Labels {
183+
labelsString += fmt.Sprintf("%s=%s", key, val)
184+
}
185+
}
186+
170187
obj := map[string]string{
171188
"MachineID": machine.ID,
172189
"Registry": machine.ImageRef.Registry,
173190
"Repository": machine.ImageRef.Repository,
174191
"Tag": machine.ImageRef.Tag,
175192
"Version": version,
176193
"Digest": machine.ImageRef.Digest,
194+
"Labels": labelsString,
177195
}
178196

179197
rows := [][]string{
@@ -183,6 +201,7 @@ func showMachineImage(ctx context.Context, app *api.AppCompact) error {
183201
machine.ImageRef.Tag,
184202
version,
185203
machine.ImageRef.Digest,
204+
labelsString,
186205
},
187206
}
188207

@@ -196,6 +215,7 @@ func showMachineImage(ctx context.Context, app *api.AppCompact) error {
196215
"Tag",
197216
"Version",
198217
"Digest",
218+
"Labels",
199219
)
200220

201221
}
@@ -265,13 +285,30 @@ func showMachineImage(ctx context.Context, app *api.AppCompact) error {
265285
version = machine.ImageVersion()
266286
}
267287

288+
var labelsString string
289+
290+
if cfg.JSONOutput {
291+
json, err := json.Marshal(image.Labels)
292+
if err != nil {
293+
return err
294+
}
295+
if string(json) != "null" {
296+
labelsString = string(json)
297+
}
298+
} else {
299+
for key, val := range image.Labels {
300+
labelsString += fmt.Sprintf("%s=%s", key, val)
301+
}
302+
}
303+
268304
objs = append(objs, map[string]string{
269305
"MachineID": machine.ID,
270306
"Registry": image.Registry,
271307
"Repository": image.Repository,
272308
"Tag": image.Tag,
273309
"Version": version,
274310
"Digest": image.Digest,
311+
"Labels": labelsString,
275312
})
276313

277314
rows = append(rows, []string{
@@ -281,6 +318,7 @@ func showMachineImage(ctx context.Context, app *api.AppCompact) error {
281318
image.Tag,
282319
version,
283320
image.Digest,
321+
labelsString,
284322
})
285323
}
286324

@@ -298,5 +336,6 @@ func showMachineImage(ctx context.Context, app *api.AppCompact) error {
298336
"Tag",
299337
"Version",
300338
"Digest",
339+
"Labels",
301340
)
302341
}

internal/env/env.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ func IsSet(keys ...string) bool {
5454
return false
5555
}
5656

57+
func IS_GH_ACTION() bool {
58+
return IsTruthy("GH_ACTIONS")
59+
}
60+
61+
func GitCommitSHA() string {
62+
sha := os.Getenv("GITHUB_SHA")
63+
return sha
64+
}
65+
66+
func GitActionEventName() string {
67+
eventName := os.Getenv("GITHUB_EVENT_NAME")
68+
return eventName
69+
}
70+
5771
// IsCI reports whether the environment is a CI one.
5872
//
5973
// Based on https://github.com/watson/ci-info/blob/c4f1553f254c78babef5c200c48569ede313b718/index.js

test/preflight/apps_v2_integration_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,3 +561,29 @@ func TestErrOutput(t *testing.T) {
561561
res = f.FlyAllowExitFailure("machine update -a %s %s -y --wait-timeout 1 --vm-size performance-1x", appName, firstMachine.ID)
562562
require.Contains(f, res.StdErrString(), "timeout reached waiting for machine's state to change")
563563
}
564+
565+
func TestImageLabel(t *testing.T) {
566+
f := testlib.NewTestEnvFromEnv(t)
567+
appName := f.CreateRandomAppName()
568+
569+
dockerfileContent := `FROM nginx:1.23.3
570+
571+
ENV BUILT_BY_DOCKERFILE=true
572+
`
573+
dockerfilePath := filepath.Join(f.WorkDir(), "Dockerfile")
574+
err := os.WriteFile(dockerfilePath, []byte(dockerfileContent), 0644)
575+
if err != nil {
576+
f.Fatalf("failed to write dockerfile at %s error: %v", dockerfilePath, err)
577+
}
578+
579+
f.Fly("launch --org %s --name %s --region %s --now --internal-port 80 --auto-confirm", f.OrgSlug(), appName, f.PrimaryRegion())
580+
f.Fly("deploy --label Z=ZZZ -a %s", appName)
581+
res := f.Fly("image show -a %s --json", appName)
582+
583+
var machineImages []map[string]string
584+
res.StdOutJSON(&machineImages)
585+
586+
for _, image := range machineImages {
587+
require.Contains(f, image["Labels"], `"Z":"ZZZ"`)
588+
}
589+
}

0 commit comments

Comments
 (0)