-
Notifications
You must be signed in to change notification settings - Fork 147
/
push.go
158 lines (130 loc) · 3.94 KB
/
push.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
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors
// Package images provides functions for building and pushing images.
package images
import (
"context"
"fmt"
"time"
"github.com/defenseunicorns/pkg/helpers"
"github.com/defenseunicorns/zarf/src/pkg/cluster"
"github.com/defenseunicorns/zarf/src/pkg/k8s"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/transform"
"github.com/defenseunicorns/zarf/src/pkg/utils"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/logs"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
// Push pushes images to a registry.
func Push(ctx context.Context, cfg PushConfig) error {
logs.Warn.SetOutput(&message.DebugWriter{})
logs.Progress.SetOutput(&message.DebugWriter{})
toPush := map[transform.Image]v1.Image{}
var totalSize int64
// Build an image list from the references
for _, refInfo := range cfg.ImageList {
img, err := utils.LoadOCIImage(cfg.SourceDirectory, refInfo)
if err != nil {
return err
}
toPush[refInfo] = img
imgSize, err := calcImgSize(img)
if err != nil {
return err
}
totalSize += imgSize
}
// If this is not a no checksum image push we will be pushing two images (the second will go faster as it checks the same layers)
if !cfg.NoChecksum {
totalSize = totalSize * 2
}
var (
err error
tunnel *k8s.Tunnel
registryURL = cfg.RegInfo.Address
)
progress := message.NewProgressBar(totalSize, fmt.Sprintf("Pushing %d images", len(toPush)))
defer progress.Stop()
if err := helpers.Retry(func() error {
c, _ := cluster.NewCluster()
if c != nil {
registryURL, tunnel, err = c.ConnectToZarfRegistryEndpoint(ctx, cfg.RegInfo)
if err != nil {
return err
}
if tunnel != nil {
defer tunnel.Close()
}
}
progress = message.NewProgressBar(totalSize, fmt.Sprintf("Pushing %d images", len(toPush)))
pushOptions := createPushOpts(cfg, progress)
pushImage := func(img v1.Image, name string) error {
if tunnel != nil {
return tunnel.Wrap(func() error { return crane.Push(img, name, pushOptions...) })
}
return crane.Push(img, name, pushOptions...)
}
pushed := []transform.Image{}
defer func() {
for _, refInfo := range pushed {
delete(toPush, refInfo)
}
}()
for refInfo, img := range toPush {
refTruncated := helpers.Truncate(refInfo.Reference, 55, true)
progress.UpdateTitle(fmt.Sprintf("Pushing %s", refTruncated))
size, err := calcImgSize(img)
if err != nil {
return err
}
// If this is not a no checksum image push it for use with the Zarf agent
if !cfg.NoChecksum {
offlineNameCRC, err := transform.ImageTransformHost(registryURL, refInfo.Reference)
if err != nil {
return err
}
message.Debugf("push %s -> %s)", refInfo.Reference, offlineNameCRC)
if err = pushImage(img, offlineNameCRC); err != nil {
return err
}
totalSize -= size
}
// To allow for other non-zarf workloads to easily see the images upload a non-checksum version
// (this may result in collisions but this is acceptable for this use case)
offlineName, err := transform.ImageTransformHostWithoutChecksum(registryURL, refInfo.Reference)
if err != nil {
return err
}
message.Debugf("push %s -> %s)", refInfo.Reference, offlineName)
if err = pushImage(img, offlineName); err != nil {
return err
}
pushed = append(pushed, refInfo)
totalSize -= size
}
return nil
}, cfg.Retries, 5*time.Second, message.Warnf); err != nil {
return err
}
progress.Successf("Pushed %d images", len(cfg.ImageList))
return nil
}
func calcImgSize(img v1.Image) (int64, error) {
size, err := img.Size()
if err != nil {
return size, err
}
layers, err := img.Layers()
if err != nil {
return size, err
}
for _, layer := range layers {
ls, err := layer.Size()
if err != nil {
return size, err
}
size += ls
}
return size, nil
}