forked from GoogleContainerTools/container-diff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
root.go
216 lines (186 loc) · 5.81 KB
/
root.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
/*
Copyright 2017 Google, Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
goflag "flag"
"fmt"
"os"
"os/user"
"path/filepath"
"sort"
"strings"
"github.com/GoogleCloudPlatform/container-diff/differs"
"github.com/GoogleCloudPlatform/container-diff/pkg/cache"
pkgutil "github.com/GoogleCloudPlatform/container-diff/pkg/util"
"github.com/GoogleCloudPlatform/container-diff/util"
"github.com/docker/docker/client"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var json bool
var save bool
var types diffTypes
var noCache bool
var LogLevel string
type validatefxn func(args []string) error
const (
DaemonPrefix = "daemon://"
RemotePrefix = "remote://"
)
var RootCmd = &cobra.Command{
Use: "container-diff",
Short: "container-diff is a tool for analyzing and comparing container images",
Long: `container-diff is a CLI tool for analyzing and comparing container images.
Images can be specified from either a local Docker daemon, or from a remote registry.
To specify a local image, prefix the image ID with 'daemon://', e.g. 'daemon://gcr.io/foo/bar'.
To specify a remote image, prefix the image ID with 'remote://', e.g. 'remote://gcr.io/foo/bar'.
If no prefix is specified, the local daemon will be checked first.
Tarballs can also be specified by simply providing the path to the .tar, .tar.gz, or .tgz file.`,
PersistentPreRun: func(c *cobra.Command, s []string) {
ll, err := logrus.ParseLevel(LogLevel)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
logrus.SetLevel(ll)
},
}
func outputResults(resultMap map[string]util.Result) {
// Outputs diff/analysis results in alphabetical order by analyzer name
sortedTypes := []string{}
for analyzerType := range resultMap {
sortedTypes = append(sortedTypes, analyzerType)
}
sort.Strings(sortedTypes)
results := make([]interface{}, len(resultMap))
for i, analyzerType := range sortedTypes {
result := resultMap[analyzerType]
if json {
results[i] = result.OutputStruct()
} else {
err := result.OutputText(analyzerType)
if err != nil {
logrus.Error(err)
}
}
}
if json {
err := util.JSONify(results)
if err != nil {
logrus.Error(err)
}
}
}
func validateArgs(args []string, validatefxns ...validatefxn) error {
for _, validatefxn := range validatefxns {
if err := validatefxn(args); err != nil {
return err
}
}
return nil
}
func checkIfValidAnalyzer(_ []string) error {
if len(types) == 0 {
types = []string{"apt"}
}
for _, name := range types {
if _, exists := differs.Analyzers[name]; !exists {
return fmt.Errorf("Argument %s is not a valid analyzer", name)
}
}
return nil
}
func getPrepperForImage(image string) (pkgutil.Prepper, error) {
cli, err := client.NewEnvClient()
if err != nil {
return nil, err
}
cacheDir, err := cacheDir()
if err != nil {
return nil, err
}
var fsCache cache.Cache
if !noCache {
fsCache, err = cache.NewFileCache(cacheDir)
if err != nil {
return nil, err
}
}
if pkgutil.IsTar(image) {
return pkgutil.TarPrepper{
Source: image,
Client: cli,
Cache: fsCache,
}, nil
} else if strings.HasPrefix(image, DaemonPrefix) {
// remove the DaemonPrefix
image := strings.Replace(image, DaemonPrefix, "", -1)
// see if the image name has tag provided, if not add latest as tag
if !strings.Contains(image, ":") {
image = image + ":latest"
}
return pkgutil.DaemonPrepper{
Source: image,
Client: cli,
Cache: fsCache,
}, nil
}
// either has remote prefix or has no prefix, in which case we force remote
return pkgutil.CloudPrepper{
Source: strings.Replace(image, RemotePrefix, "", -1),
Client: cli,
Cache: fsCache,
}, nil
}
func cacheDir() (string, error) {
user, err := user.Current()
if err != nil {
return "", err
}
rootDir := filepath.Join(user.HomeDir, ".container-diff")
return filepath.Join(rootDir, "cache"), nil
}
func init() {
RootCmd.PersistentFlags().StringVarP(&LogLevel, "verbosity", "v", "warning", "This flag controls the verbosity of container-diff.")
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
}
// Define a type named "diffSlice" as a slice of strings
type diffTypes []string
// Now, for our new type, implement the two methods of
// the flag.Value interface...
// The first method is String() string
func (d *diffTypes) String() string {
return strings.Join(*d, ",")
}
// The second method is Set(value string) error
func (d *diffTypes) Set(value string) error {
// Dedupe repeated elements.
for _, t := range *d {
if t == value {
return nil
}
}
*d = append(*d, value)
return nil
}
func (d *diffTypes) Type() string {
return "Diff Types"
}
func addSharedFlags(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&json, "json", "j", false, "JSON Output defines if the diff should be returned in a human readable format (false) or a JSON (true).")
cmd.Flags().VarP(&types, "type", "t", "This flag sets the list of analyzer types to use. Set it repeatedly to use multiple analyzers.")
cmd.Flags().BoolVarP(&save, "save", "s", false, "Set this flag to save rather than remove the final image filesystems on exit.")
cmd.Flags().BoolVarP(&util.SortSize, "order", "o", false, "Set this flag to sort any file/package results by descending size. Otherwise, they will be sorted by name.")
cmd.Flags().BoolVarP(&noCache, "no-cache", "n", false, "Set this to force retrieval of layers on each run.")
}