-
-
Notifications
You must be signed in to change notification settings - Fork 284
/
verify.go
141 lines (112 loc) · 3.29 KB
/
verify.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
package chart
import (
"fmt"
"os"
"path"
"text/tabwriter"
"github.com/alexellis/arkade/pkg/helm"
"github.com/spf13/cobra"
"github.com/google/go-containerregistry/pkg/crane"
)
func MakeVerify() *cobra.Command {
var command = &cobra.Command{
Use: "verify",
Short: "Verify images from a values.yaml file exist on the remote registry",
Long: `Verify images in a values.yaml file exist within a remote registry.
Container images must be specified at the top level, or one level down in the
"image: " or "component.image: " field in a values.yaml file.
Returns exit code zero if all images were found on the remote registry.
Otherwise, it returns a non-zero exit code and a table of images not found:
COMPONENT IMAGE
dashboard ghcr.io/openfaasltd/openfaas-dashboard:0.9.8
autoscaler ghcr.io/openfaasltd/autoscaler:0.2.5
`,
Example: ` chartctl verify -f ./chart/values.yaml
chartctl verify --verbose -f ./chart/values.yaml`,
SilenceUsage: true,
}
command.Flags().StringP("file", "f", "", "Path to values.yaml file")
command.Flags().BoolP("verbose", "v", false, "Verbose output")
command.Flags().IntP("depth", "d", 3, "how many levels deep into the YAML structure to walk looking for image: tags")
command.PreRunE = func(cmd *cobra.Command, args []string) error {
_, err := cmd.Flags().GetInt("depth")
if err != nil {
return fmt.Errorf("error with --depth usage: %s", err)
}
return nil
}
command.RunE = func(cmd *cobra.Command, args []string) error {
file, err := cmd.Flags().GetString("file")
if err != nil {
return fmt.Errorf("invalid value for flag --file")
}
verbose, _ := cmd.Flags().GetBool("verbose")
depth, _ := cmd.Flags().GetInt("depth")
if len(file) == 0 {
return fmt.Errorf("flag --file is required")
}
if ext := path.Ext(file); ext != ".yaml" && ext != ".yml" {
return fmt.Errorf("--file must be a YAML file")
}
if verbose {
fmt.Printf("Verifying images in: %s\n", file)
}
values, err := helm.Load(file)
if err != nil {
return err
}
filtered := helm.FilterImagesUptoDepth(values, depth)
if len(filtered) == 0 {
return fmt.Errorf("no images found in %s", file)
}
missed := []verifyError{}
if verbose {
if len(filtered) > 0 {
fmt.Printf("Found %d images\n", len(filtered))
}
}
for k, v := range filtered {
if verbose {
fmt.Printf("> [%s] %s\n", k, v)
}
ref, err := crane.Head(v)
if err != nil {
missed = append(missed, verifyError{
Err: err,
Image: v,
Component: k,
})
} else {
if verbose {
fmt.Printf("< [%s] %v\n", v, ref.Digest)
}
}
}
if len(missed) > 0 {
fmt.Fprintf(os.Stderr, "%d images are missing in %s\n\n", len(missed), file)
w := tabwriter.NewWriter(os.Stderr, 20, 3, 1, ' ', 0)
if verbose {
fmt.Fprintf(w, "COMPONENT\tIMAGE\tERROR\n")
} else {
fmt.Fprintf(w, "COMPONENT\tIMAGE\n")
}
for _, err := range missed {
if verbose {
fmt.Fprintf(w, "%s\t%s\t%s\n", err.Component, err.Image, err.Err)
} else {
fmt.Fprintf(w, "%s\t%s\n", err.Component, err.Image)
}
w.Flush()
}
fmt.Fprintf(os.Stderr, "\n")
return fmt.Errorf("verifying failed")
}
return nil
}
return command
}
type verifyError struct {
Err error
Image string
Component string
}