-
Notifications
You must be signed in to change notification settings - Fork 105
/
validatepullsecret.go
201 lines (178 loc) · 6.76 KB
/
validatepullsecret.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
package cluster
import (
"context"
"fmt"
v1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1"
"github.com/openshift/osdctl/cmd/servicelog"
"github.com/openshift/osdctl/pkg/k8s"
"github.com/openshift/osdctl/pkg/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"os"
)
var BackplaneClusterAdmin = "backplane-cluster-admin"
// validatePullSecretOptions defines the struct for running validate-pull-secret command
type validatePullSecretOptions struct {
clusterID string
elevate bool
kubeCli *k8s.LazyClient
reason string
}
func newCmdValidatePullSecret(kubeCli *k8s.LazyClient) *cobra.Command {
ops := newValidatePullSecretOptions(kubeCli)
validatePullSecretCmd := &cobra.Command{
Use: "validate-pull-secret [CLUSTER_ID]",
Short: "Checks if the pull secret email matches the owner email",
Long: `Checks if the pull secret email matches the owner email.
The owner's email to check will be determined by the cluster identifier passed to the command, while the pull secret checked will be determined by the cluster that the caller is currently logged in to.
`,
Args: cobra.ExactArgs(1),
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
ops.clusterID = args[0]
cmdutil.CheckErr(ops.run())
},
}
validatePullSecretCmd.Flags().StringVar(&ops.reason, "reason", "", "The reason for this command to be run (usualy an OHSS or PD ticket), mandatory when using elevate")
_ = validatePullSecretCmd.MarkFlagRequired("reason")
return validatePullSecretCmd
}
func newValidatePullSecretOptions(client *k8s.LazyClient) *validatePullSecretOptions {
return &validatePullSecretOptions{
kubeCli: client,
}
}
func (o *validatePullSecretOptions) run() error {
// get the pull secret in OCM
emailOCM, err, done := o.getPullSecretFromOCM()
if err != nil {
return err
}
if done {
return nil
}
// get the pull secret in cluster
emailCluster, err, done := getPullSecretElevated(o.clusterID, o.kubeCli, o.reason)
if err != nil {
return err
}
if done {
return nil
}
if emailOCM != emailCluster {
_, _ = fmt.Fprintln(os.Stderr, "Pull secret email doesn't match OCM user email. Sending service log.")
postCmd := servicelog.PostCmdOptions{
Template: "https://raw.githubusercontent.com/openshift/managed-notifications/master/osd/pull_secret_user_mismatch.json",
ClusterId: o.clusterID,
}
return postCmd.Run()
}
fmt.Println("Email addresses match.")
return nil
}
// getPullSecretElevated gets the pull-secret in the cluster
// with backplane elevation.
func getPullSecretElevated(clusterID string, kubeCli *k8s.LazyClient, reason string) (email string, err error, sentSL bool) {
fmt.Println("Getting the pull-secret in the cluster with elevated permissions")
kubeCli.Impersonate(BackplaneClusterAdmin, reason, fmt.Sprintf("Elevation required to get pull secret email to check if it matches the owner email for %s cluster", clusterID))
secret := &corev1.Secret{}
if err := kubeCli.Get(context.TODO(), types.NamespacedName{Namespace: "openshift-config", Name: "pull-secret"}, secret); err != nil {
return "", err, false
}
clusterPullSecretEmail, err, done := getPullSecretEmail(clusterID, secret, true)
if done {
return "", err, true
}
fmt.Printf("email from cluster: %s\n", clusterPullSecretEmail)
return clusterPullSecretEmail, nil, false
}
// getPullSecretFromOCM gets the cluster owner email from OCM
// it returns the email, error and done
// done means a service log has been sent
func (o *validatePullSecretOptions) getPullSecretFromOCM() (string, error, bool) {
fmt.Println("Getting email from OCM")
ocm, err := utils.CreateConnection()
if err != nil {
return "", err, false
}
defer func() {
if ocmCloseErr := ocm.Close(); ocmCloseErr != nil {
_, _ = fmt.Fprintf(os.Stderr, "Cannot close the ocm (possible memory leak): %q", ocmCloseErr)
}
}()
subscription, err := utils.GetSubscription(ocm, o.clusterID)
if err != nil {
return "", err, false
}
account, err := utils.GetAccount(ocm, subscription.Creator().ID())
if err != nil {
return "", err, false
}
// validate the registryCredentials before return
registryCredentials, err := utils.GetRegistryCredentials(ocm, account.ID())
if err != nil {
return "", err, false
}
if len(registryCredentials) == 0 {
_, _ = fmt.Fprintln(os.Stderr, "There is no pull secret in OCM. Sending service log.")
postCmd := servicelog.PostCmdOptions{
Template: "https://raw.githubusercontent.com/openshift/managed-notifications/master/osd/update_pull_secret.json",
TemplateParams: []string{"REGISTRY=registry.redhat.io"},
ClusterId: o.clusterID,
}
if err = postCmd.Run(); err != nil {
return "", err, false
}
return "", nil, true
}
fmt.Printf("email from OCM: %s\n", account.Email())
return account.Email(), nil, false
}
// getPullSecretEmail extract the email from the pull-secret secret in cluster
func getPullSecretEmail(clusterID string, secret *corev1.Secret, sendServiceLog bool) (string, error, bool) {
dockerConfigJsonBytes, found := secret.Data[".dockerconfigjson"]
if !found {
// Indicates issue w/ pull-secret, so we can stop evaluating and specify a more direct course of action
_, _ = fmt.Fprintln(os.Stderr, "Secret does not contain expected key '.dockerconfigjson'. Sending service log.")
if sendServiceLog {
postCmd := servicelog.PostCmdOptions{
Template: "https://raw.githubusercontent.com/openshift/managed-notifications/master/osd/pull_secret_change_breaking_upgradesync.json",
ClusterId: clusterID,
}
if err := postCmd.Run(); err != nil {
return "", err, true
}
}
return "", nil, true
}
dockerConfigJson, err := v1.UnmarshalAccessToken(dockerConfigJsonBytes)
if err != nil {
return "", err, true
}
cloudOpenshiftAuth, found := dockerConfigJson.Auths()["cloud.openshift.com"]
if !found {
_, _ = fmt.Fprintln(os.Stderr, "Secret does not contain entry for cloud.openshift.com")
if sendServiceLog {
fmt.Println("Sending service log")
postCmd := servicelog.PostCmdOptions{
Template: "https://raw.githubusercontent.com/openshift/managed-notifications/master/osd/pull_secret_change_breaking_upgradesync.json",
ClusterId: clusterID,
}
if err = postCmd.Run(); err != nil {
return "", err, true
}
}
return "", nil, true
}
clusterPullSecretEmail := cloudOpenshiftAuth.Email()
if clusterPullSecretEmail == "" {
_, _ = fmt.Fprintf(os.Stderr, "%v\n%v\n%v\n",
"Couldn't extract email address from pull secret for cloud.openshift.com",
"This can mean the pull secret is misconfigured. Please verify the pull secret manually:",
" oc get secret -n openshift-config pull-secret -o json | jq -r '.data[\".dockerconfigjson\"]' | base64 -d")
return "", nil, true
}
return clusterPullSecretEmail, nil, false
}