-
Notifications
You must be signed in to change notification settings - Fork 3
/
main.go
145 lines (132 loc) · 4.05 KB
/
main.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
package main
import (
"encoding/json"
"flag"
"fmt"
"github.com/sirupsen/logrus"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/estesp/manifest-tool/v2/pkg/registry"
"github.com/estesp/manifest-tool/v2/pkg/store"
"github.com/estesp/manifest-tool/v2/pkg/types"
"github.com/estesp/manifest-tool/v2/pkg/util"
)
var (
baseImage, image string
baseImageRegistryUsername, baseImageRegistryPassword string
imageRegistryUsername, imageRegistryPassword string
)
func init() {
flag.StringVar(&baseImage, "base-image", "", "Base Image")
flag.StringVar(&image, "image", "", "Image")
flag.StringVar(&baseImageRegistryUsername, "base-reg-username", "", "Base Image Registry Username")
flag.StringVar(&baseImageRegistryPassword, "base-reg-password", "", "Base Image Registry Password")
flag.StringVar(&imageRegistryUsername, "image-reg-username", "", "Image Registry Username")
flag.StringVar(&imageRegistryPassword, "image-reg-password", "", "Image Registry Password")
flag.Parse()
}
func main() {
if baseImage == "" || image == "" {
fmt.Println("::error ::baseImage and image should be set")
return
}
baseLayers, err := parseImage(baseImage, baseImageRegistryUsername, baseImageRegistryPassword)
if err != nil {
fmt.Printf("::error ::failed to get layers for the base image, err: %v\n", err)
return
}
imageLayers, err := parseImage(image, imageRegistryUsername, imageRegistryPassword)
if err != nil {
fmt.Printf("::error ::failed to get layers for the image, err: %v\n", err)
return
}
for _, imageLayer := range imageLayers {
found := false
for _, baseLayer := range baseLayers {
found = subset(baseLayer, imageLayer)
if found {
break
}
}
if !found {
fmt.Println("::set-output name=needs-update::true")
return
}
}
fmt.Println("::set-output name=needs-update::false")
}
func subset(a, b []digest.Digest) bool {
if len(a) > len(b) {
return false
}
for i, l := range a {
if l != b[i] {
return false
}
}
return true
}
func parseImage(name, username, password string) (digests [][]digest.Digest, err error) {
resolver := util.NewResolver(username, password, false,
false)
memoryStore := store.NewMemoryStore()
imageRef, err := util.ParseName(name)
if err != nil {
logrus.Fatal(err)
}
descriptor, err := registry.FetchDescriptor(resolver, memoryStore, imageRef)
if err != nil {
return nil, err
}
_, db, _ := memoryStore.Get(descriptor)
switch descriptor.MediaType {
case ocispec.MediaTypeImageIndex, types.MediaTypeDockerSchema2ManifestList:
// this is a multi-platform image descriptor; marshal to Index type
var idx ocispec.Index
if err := json.Unmarshal(db, &idx); err != nil {
return nil, err
}
digests, err = parseList(memoryStore, idx)
if err != nil {
return nil, fmt.Errorf("failed to parse the manifest list: %w", err)
}
case ocispec.MediaTypeImageManifest, types.MediaTypeDockerSchema2Manifest:
var man ocispec.Manifest
if err := json.Unmarshal(db, &man); err != nil {
return nil, err
}
_, cb, _ := memoryStore.Get(man.Config)
var conf ocispec.Image
if err := json.Unmarshal(cb, &conf); err != nil {
return nil, err
}
dig := getDigests(man.Layers)
digests = append(digests, dig)
default:
return nil, fmt.Errorf("unknown descriptor type: %s", descriptor.MediaType)
}
return
}
func parseList(cs *store.MemoryStore, index ocispec.Index) (digests [][]digest.Digest, err error) {
for _, img := range index.Manifests {
_, db, _ := cs.Get(img)
switch img.MediaType {
case ocispec.MediaTypeImageManifest, types.MediaTypeDockerSchema2Manifest:
var man ocispec.Manifest
if err := json.Unmarshal(db, &man); err != nil {
return nil, err
}
dig := getDigests(man.Layers)
digests = append(digests, dig)
default:
return nil, fmt.Errorf("Unknown media type for further display: %s\n", img.MediaType)
}
}
return
}
func getDigests(layers []ocispec.Descriptor) (digests []digest.Digest) {
for _, layer := range layers {
digests = append(digests, layer.Digest)
}
return
}