-
Notifications
You must be signed in to change notification settings - Fork 88
/
admin-console-push-images.go
154 lines (126 loc) · 4.35 KB
/
admin-console-push-images.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
package cli
import (
"encoding/base64"
"fmt"
"net/url"
"os"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/pkg/errors"
"github.com/replicatedhq/kots/pkg/docker/registry"
"github.com/replicatedhq/kots/pkg/kotsadm"
kotsadmtypes "github.com/replicatedhq/kots/pkg/kotsadm/types"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func AdminPushImagesCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "push-images [airgap filename] [registry host]",
Short: "Push admin console images",
Long: "Push admin console images from airgap bundle to a private registry",
SilenceUsage: true,
SilenceErrors: false,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
},
RunE: func(cmd *cobra.Command, args []string) error {
v := viper.GetViper()
if len(args) != 2 {
cmd.Help()
os.Exit(1)
}
airgapArchive := args[0]
endpoint := args[1]
endpointParts := strings.Split(endpoint, "/")
if len(endpointParts) != 2 {
return errors.New("registry must be in the format <endpoint>/<namespace>")
}
username := v.GetString("registry-username")
password := v.GetString("registry-password")
if isECR(endpoint) && username != "AWS" {
var err error
username, password, err = getECRLogin(endpoint, username, password)
if err != nil {
return errors.Wrap(err, "failed get ecr login")
}
}
options := kotsadmtypes.PushImagesOptions{
KotsadmTag: v.GetString("kotsadm-tag"),
Registry: registry.RegistryOptions{
Endpoint: args[1],
Username: username,
Password: password,
},
ProgressWriter: os.Stdout,
}
err := kotsadm.PushImages(airgapArchive, options)
if err != nil {
return errors.Wrap(err, "failed to push images")
}
return nil
},
}
cmd.Flags().String("registry-username", "", "user name to use to authenticate with the registry")
cmd.Flags().String("registry-password", "", "password to use to authenticate with the registry")
cmd.Flags().String("kotsadm-tag", "", "set to override the tag of kotsadm. this may create an incompatible deployment because the version of kots and kotsadm are designed to work together")
cmd.Flags().MarkHidden("kotsadm-tag")
return cmd
}
func isECR(endpoint string) bool {
if !strings.HasPrefix(endpoint, "http") {
// url.Parse doesn't work without scheme
endpoint = fmt.Sprintf("https://%s", endpoint)
}
parsed, err := url.Parse(endpoint)
if err != nil {
return false
}
return strings.HasSuffix(parsed.Hostname(), ".amazonaws.com")
}
func getECRLogin(endpoint string, keyID string, accessKey string) (string, string, error) {
registry, zone, err := parseECREndpoint(endpoint)
if err != nil {
return "", "", errors.Wrap(err, "failed to parse ECR endpoint")
}
ecrService := getECRService(keyID, accessKey, zone)
ecrToken, err := ecrService.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{
RegistryIds: []*string{
®istry,
},
})
if err != nil {
return "", "", errors.Wrap(err, "failed to get ecr token")
}
if len(ecrToken.AuthorizationData) == 0 {
return "", "", errors.Errorf("repo %s not accessible with specified credentials", endpoint)
}
decoded, err := base64.StdEncoding.DecodeString(*ecrToken.AuthorizationData[0].AuthorizationToken)
if err != nil {
return "", "", errors.Wrap(err, "failed to decode ecr token")
}
parts := strings.Split(string(decoded), ":")
if len(parts) != 2 {
return "", "", errors.New("ecr token is not in user:password format")
}
username := parts[0]
password := parts[1]
return username, password, nil
}
func getECRService(accessKeyID, secretAccessKey, zone string) *ecr.ECR {
awsConfig := &aws.Config{Region: aws.String(zone)}
awsConfig.Credentials = credentials.NewStaticCredentials(accessKeyID, secretAccessKey, "")
return ecr.New(session.New(awsConfig))
}
func parseECREndpoint(endpoint string) (registry, zone string, err error) {
splitEndpoint := strings.Split(endpoint, ".")
if len(splitEndpoint) < 6 {
return "", "", errors.Errorf("invalid ecr endpoint: %s", endpoint)
}
if splitEndpoint[1] != "dkr" || splitEndpoint[2] != "ecr" {
return "", "", errors.Errorf("only dkr and ecr endpoints are supported: %s", endpoint)
}
return splitEndpoint[0], splitEndpoint[3], nil
}