From ed0ad7ef019c8f55a2ef62df9032e0267f7c39fc Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Fri, 19 Jul 2019 21:21:00 +0000 Subject: [PATCH] Image pull secrets --- cmd/preflight/cli/interactive_results.go | 4 +- cmd/preflight/cli/run_nocrd.go | 13 +++- .../troubleshoot_v1beta1_preflight.yaml | 73 +++++++++++-------- go.mod | 1 - pkg/analyze/analyzer.go | 18 +++-- pkg/analyze/image_pull_secret.go | 49 +++++++++++++ .../troubleshoot/v1beta1/analyzer_shared.go | 7 ++ .../v1beta1/zz_generated.deepcopy.go | 29 ++++++++ pkg/collect/cluster_resources.go | 56 ++++++++++++++ pkg/collect/runner.go | 2 +- .../collectorjob/collector_server.go | 2 +- .../collectorjob/collectorjob_controller.go | 2 +- pkg/preflight/server.go | 2 +- 13 files changed, 213 insertions(+), 45 deletions(-) create mode 100644 pkg/analyze/image_pull_secret.go diff --git a/cmd/preflight/cli/interactive_results.go b/cmd/preflight/cli/interactive_results.go index f050e166..3225c1a5 100644 --- a/cmd/preflight/cli/interactive_results.go +++ b/cmd/preflight/cli/interactive_results.go @@ -272,9 +272,11 @@ func showSaved(filename string) { func appName(preflightName string) string { words := strings.Split(strings.Title(strings.Replace(preflightName, "-", " ", -1)), " ") casedWords := []string{} - for _, word := range words { + for i, word := range words { if strings.ToLower(word) == "ai" { casedWords = append(casedWords, "AI") + } else if strings.ToLower(word) == "io" && i > 0 { + casedWords[i-1] += ".io" } else { casedWords = append(casedWords, word) } diff --git a/cmd/preflight/cli/run_nocrd.go b/cmd/preflight/cli/run_nocrd.go index b2f1e306..6e491730 100644 --- a/cmd/preflight/cli/run_nocrd.go +++ b/cmd/preflight/cli/run_nocrd.go @@ -11,6 +11,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "time" analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze" @@ -75,10 +76,20 @@ func runPreflightsNoCRD(v *viper.Viper, arg string) error { return contents, nil } + getChildCollectedFileContents := func(prefix string) (map[string][]byte, error) { + matching := make(map[string][]byte) + for k, v := range allCollectedData { + if strings.HasPrefix(k, prefix) { + matching[k] = v + } + } + + return matching, nil + } analyzeResults := []*analyzerunner.AnalyzeResult{} for _, analyzer := range preflight.Spec.Analyzers { - analyzeResult, err := analyzerunner.Analyze(analyzer, getCollectedFileContents) + analyzeResult, err := analyzerunner.Analyze(analyzer, getCollectedFileContents, getChildCollectedFileContents) if err != nil { fmt.Printf("an analyzer failed to run: %v\n", err) continue diff --git a/config/samples/troubleshoot_v1beta1_preflight.yaml b/config/samples/troubleshoot_v1beta1_preflight.yaml index 969c119f..dbf6ee5d 100644 --- a/config/samples/troubleshoot_v1beta1_preflight.yaml +++ b/config/samples/troubleshoot_v1beta1_preflight.yaml @@ -1,41 +1,50 @@ apiVersion: troubleshoot.replicated.com/v1beta1 kind: Preflight metadata: - name: shiny-new-ai + name: support-io spec: uploadResultsTo: https://hookb.in/Z26mz8R9VpC7q7eYrWob analyzers: - - clusterVersion: + - imagePullSecret: + checkName: "blarg" + registryName: quay.io outcomes: - fail: - when: "< 1.13.0" - message: Sorry, ShinyNew.ai requires at least Kubernetes 1.14.0. Please update your Kubernetes cluster before installing. - uri: https://enterprise.shinynew.ai/install/requirements/kubernetes - - warn: - when: "< 1.15.0" - message: The version of Kubernetes you are running meets the minimum requirements to run ShineyNew.ai. It's recommended to run Kubernetes 1.15.0 or later. - uri: https://enterprise.shinynew.ai/install/requirements/kubernetes + message: Cannot pull from quay.io - pass: - message: The version of Kubernetes you have installed meets the required and recommended versions. - - storageClass: - checkName: Required storage classes - storageClassName: "microk8s-hostpath" - outcomes: - - fail: - message: The required storage class was not found in the cluster. - - pass: - message: The required storage class was found in the cluster. - - ingress: - namespace: default - ingressName: my-app-ingress - outcomes: - - fail: - message: Expected to find an ingress named "my-app-ingress". - - pass: - message: Expected ingress was found. - customResourceDefinitionName: rook - outcomes: - - fail: - message: Rook is required for ShinyNew.ai. Rook was not found in the cluster. - - pass: - message: Found a supported version of Rook installed and running in the cluster. + message: Found credentials to pull from quay.io + # - clusterVersion: + # outcomes: + # - fail: + # when: "< 1.13.0" + # message: Sorry, support.io requires at least Kubernetes 1.14.0. Please update your Kubernetes cluster before installing. + # uri: https://enterprise.support.io/install/requirements/kubernetes + # - warn: + # when: "< 1.15.0" + # message: The version of Kubernetes you are running meets the minimum requirements to run support.io. It's recommended to run Kubernetes 1.15.0 or later. + # uri: https://enterprise.support.io/install/requirements/kubernetes + # - pass: + # message: The version of Kubernetes you have installed meets the required and recommended versions. + # - storageClass: + # checkName: Required storage classes + # storageClassName: "microk8s-hostpath" + # outcomes: + # - fail: + # message: The required storage class was not found in the cluster. + # - pass: + # message: The required storage class was found in the cluster. + # - ingress: + # namespace: default + # ingressName: my-app-ingress + # outcomes: + # - fail: + # message: Expected to find an ingress named "my-app-ingress". + # - pass: + # message: Expected ingress was found. + # - customResourceDefinitionName: + # customResourceDefinitionName: rook + # outcomes: + # - fail: + # message: Rook is required for Support.io. Rook was not found in the cluster. + # - pass: + # message: Found a supported version of Rook installed and running in the cluster. diff --git a/go.mod b/go.mod index a867a023..e0a3bd9d 100644 --- a/go.mod +++ b/go.mod @@ -32,5 +32,4 @@ require ( k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible sigs.k8s.io/controller-runtime v0.2.0-beta.2 sigs.k8s.io/controller-tools v0.2.0-beta.2 // indirect - ) diff --git a/pkg/analyze/analyzer.go b/pkg/analyze/analyzer.go index 79c40f9a..4a72fb5f 100644 --- a/pkg/analyze/analyzer.go +++ b/pkg/analyze/analyzer.go @@ -16,21 +16,27 @@ type AnalyzeResult struct { URI string } -func Analyze(analyzer *troubleshootv1beta1.Analyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) { +type getCollectedFileContents func(string) ([]byte, error) +type getChildCollectedFileContents func(string) (map[string][]byte, error) + +func Analyze(analyzer *troubleshootv1beta1.Analyze, getFile getCollectedFileContents, findFiles getChildCollectedFileContents) (*AnalyzeResult, error) { if analyzer.ClusterVersion != nil { - return analyzeClusterVersion(analyzer.ClusterVersion, getCollectedFileContents) + return analyzeClusterVersion(analyzer.ClusterVersion, getFile) } if analyzer.StorageClass != nil { - return analyzeStorageClass(analyzer.StorageClass, getCollectedFileContents) + return analyzeStorageClass(analyzer.StorageClass, getFile) } if analyzer.CustomResourceDefinition != nil { - return analyzeCustomResourceDefinition(analyzer.CustomResourceDefinition, getCollectedFileContents) + return analyzeCustomResourceDefinition(analyzer.CustomResourceDefinition, getFile) } if analyzer.Ingress != nil { - return analyzeIngress(analyzer.Ingress, getCollectedFileContents) + return analyzeIngress(analyzer.Ingress, getFile) } if analyzer.Secret != nil { - return analyzeSecret(analyzer.Secret, getCollectedFileContents) + return analyzeSecret(analyzer.Secret, getFile) + } + if analyzer.ImagePullSecret != nil { + return analyzeImagePullSecret(analyzer.ImagePullSecret, findFiles) } return nil, errors.New("invalid analyzer") diff --git a/pkg/analyze/image_pull_secret.go b/pkg/analyze/image_pull_secret.go new file mode 100644 index 00000000..3a5d263a --- /dev/null +++ b/pkg/analyze/image_pull_secret.go @@ -0,0 +1,49 @@ +package analyzer + +import ( + "encoding/json" + + troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1" +) + +func analyzeImagePullSecret(analyzer *troubleshootv1beta1.ImagePullSecret, getChildCollectedFileContents func(string) (map[string][]byte, error)) (*AnalyzeResult, error) { + imagePullSecrets, err := getChildCollectedFileContents("cluster-resources/image-pull-secrets") + if err != nil { + return nil, err + } + + var failOutcome *troubleshootv1beta1.Outcome + var passOutcome *troubleshootv1beta1.Outcome + for _, outcome := range analyzer.Outcomes { + if outcome.Fail != nil { + failOutcome = outcome + } else if outcome.Pass != nil { + passOutcome = outcome + } + } + + result := AnalyzeResult{ + Title: analyzer.CheckName, + IsFail: true, + Message: failOutcome.Fail.Message, + URI: failOutcome.Fail.URI, + } + + for _, v := range imagePullSecrets { + registryAndUsername := make(map[string]string) + if err := json.Unmarshal(v, ®istryAndUsername); err != nil { + return nil, err + } + + for registry, _ := range registryAndUsername { + if registry == analyzer.RegistryName { + result.IsPass = true + result.IsFail = false + result.Message = passOutcome.Pass.Message + result.URI = passOutcome.Pass.URI + } + } + } + + return &result, nil +} diff --git a/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go b/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go index 96cf4c78..fbfe9989 100644 --- a/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go +++ b/pkg/apis/troubleshoot/v1beta1/analyzer_shared.go @@ -44,6 +44,12 @@ type AnalyzeSecret struct { Key string `json:"key,omitempty" yaml:"key,omitempty"` } +type ImagePullSecret struct { + AnalyzeMeta `json:",inline" yaml:",inline"` + Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"` + RegistryName string `json:"registryName" yaml:"registryName"` +} + type AnalyzeMeta struct { CheckName string `json:"checkName,omitempty" yaml:"checkName,omitempty"` } @@ -54,4 +60,5 @@ type Analyze struct { CustomResourceDefinition *CustomResourceDefinition `json:"customResourceDefinition,omitempty" yaml:"customResourceDefinition,omitempty"` Ingress *Ingress `json:"ingress,omitempty" yaml:"ingress,omitempty"` Secret *AnalyzeSecret `json:"secret,omitempty" yaml:"secret,omitempty"` + ImagePullSecret *ImagePullSecret `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"` } diff --git a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go index fca06402..977a5129 100644 --- a/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta1/zz_generated.deepcopy.go @@ -52,6 +52,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) { *out = new(AnalyzeSecret) (*in).DeepCopyInto(*out) } + if in.ImagePullSecret != nil { + in, out := &in.ImagePullSecret, &out.ImagePullSecret + *out = new(ImagePullSecret) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze. @@ -648,6 +653,7 @@ func (in *CustomResourceDefinition) DeepCopy() *CustomResourceDefinition { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +<<<<<<< HEAD func (in *Exec) DeepCopyInto(out *Exec) { *out = *in if in.Selector != nil { @@ -685,10 +691,25 @@ func (in *Get) DeepCopyInto(out *Get) { *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val +======= +func (in *ImagePullSecret) DeepCopyInto(out *ImagePullSecret) { + *out = *in + out.AnalyzeMeta = in.AnalyzeMeta + if in.Outcomes != nil { + in, out := &in.Outcomes, &out.Outcomes + *out = make([]*Outcome, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Outcome) + (*in).DeepCopyInto(*out) + } +>>>>>>> Image pull secrets } } } +<<<<<<< HEAD // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Get. func (in *Get) DeepCopy() *Get { if in == nil { @@ -725,6 +746,14 @@ func (in *HTTP) DeepCopy() *HTTP { return nil } out := new(HTTP) +======= +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImagePullSecret. +func (in *ImagePullSecret) DeepCopy() *ImagePullSecret { + if in == nil { + return nil + } + out := new(ImagePullSecret) +>>>>>>> Image pull secrets in.DeepCopyInto(out) return out } diff --git a/pkg/collect/cluster_resources.go b/pkg/collect/cluster_resources.go index 8cb7265e..4e4113ee 100644 --- a/pkg/collect/cluster_resources.go +++ b/pkg/collect/cluster_resources.go @@ -1,8 +1,10 @@ package collect import ( + "encoding/base64" "encoding/json" "fmt" + "strings" "github.com/replicatedhq/troubleshoot/pkg/redact" corev1 "k8s.io/api/core/v1" @@ -20,6 +22,7 @@ type ClusterResourcesOutput struct { Ingress map[string][]byte `json:"cluster-resources/ingress,omitempty"` StorageClasses []byte `json:"cluster-resources/storage-classes.json,omitempty"` CustomResourceDefinitions []byte `json:"cluster-resources/custom-resource-definitions.json,omitempty"` + ImagePullSecrets map[string][]byte `json:"cluster-resources/image-pull-secrets,omitempty"` } func ClusterResources(redact bool) error { @@ -92,6 +95,13 @@ func ClusterResources(redact bool) error { } clusterResourcesOutput.CustomResourceDefinitions = customResourceDefinitions + // imagepullsecrets + imagePullSecrets, err := imagePullSecrets(client, namespaceNames) + if err != nil { + return err + } + clusterResourcesOutput.ImagePullSecrets = imagePullSecrets + if redact { clusterResourcesOutput, err = clusterResourcesOutput.Redact() if err != nil { @@ -231,6 +241,51 @@ func crds(client *apiextensionsv1beta1clientset.ApiextensionsV1beta1Client) ([]b return b, nil } +func imagePullSecrets(client *kubernetes.Clientset, namespaces []string) (map[string][]byte, error) { + imagePullSecrets := make(map[string][]byte) + + // better than vendoring in.... kubernetes + type DockerConfigEntry struct { + Auth string `json:"auth"` + } + type DockerConfigJSON struct { + Auths map[string]DockerConfigEntry `json:"auths"` + } + + for _, namespace := range namespaces { + secrets, err := client.CoreV1().Secrets(namespace).List(metav1.ListOptions{}) + if err != nil { + return nil, err + } + + for _, secret := range secrets.Items { + if secret.Type == corev1.SecretTypeDockerConfigJson { + dockerConfigJSON := DockerConfigJSON{} + if err := json.Unmarshal(secret.Data[corev1.DockerConfigJsonKey], &dockerConfigJSON); err != nil { + return nil, err + } + + for registry, registryAuth := range dockerConfigJSON.Auths { + decoded, err := base64.StdEncoding.DecodeString(registryAuth.Auth) + if err != nil { + return nil, err + } + + registryAndUsername := make(map[string]string) + registryAndUsername[registry] = strings.Split(string(decoded), ":")[0] + b, err := json.Marshal(registryAndUsername) + if err != nil { + return nil, err + } + imagePullSecrets[fmt.Sprintf("%s/%s.json", namespace, secret.Name)] = b + } + } + } + } + + return imagePullSecrets, nil +} + func (c *ClusterResourcesOutput) Redact() (*ClusterResourcesOutput, error) { namespaces, err := redact.Redact(c.Namespaces) if err != nil { @@ -268,5 +323,6 @@ func (c *ClusterResourcesOutput) Redact() (*ClusterResourcesOutput, error) { Ingress: ingress, StorageClasses: storageClasses, CustomResourceDefinitions: crds, + ImagePullSecrets: c.ImagePullSecrets, }, nil } diff --git a/pkg/collect/runner.go b/pkg/collect/runner.go index 9cb95223..a9706656 100644 --- a/pkg/collect/runner.go +++ b/pkg/collect/runner.go @@ -89,7 +89,7 @@ func createCollectorPod(client client.Client, scheme *runtime.Scheme, ownerRef m return nil, err } - imageName := "replicatedhq/troubleshoot:latest" + imageName := "replicated/troubleshoot:latest" imagePullPolicy := corev1.PullAlways if image != "" { diff --git a/pkg/controller/collectorjob/collector_server.go b/pkg/controller/collectorjob/collector_server.go index 29cbc29a..0d0fae10 100644 --- a/pkg/controller/collectorjob/collector_server.go +++ b/pkg/controller/collectorjob/collector_server.go @@ -28,7 +28,7 @@ func (r *ReconcileCollectorJob) createCollectorServer(instance *troubleshootv1be return err } - imageName := "replicatedhq/troubleshoot:latest" + imageName := "replicated/troubleshoot:latest" imagePullPolicy := corev1.PullAlways if instance.Spec.Image != "" { diff --git a/pkg/controller/collectorjob/collectorjob_controller.go b/pkg/controller/collectorjob/collectorjob_controller.go index 7d086d26..14518bf6 100644 --- a/pkg/controller/collectorjob/collectorjob_controller.go +++ b/pkg/controller/collectorjob/collectorjob_controller.go @@ -359,7 +359,7 @@ func (r *ReconcileCollectorJob) createCollectorPod(instance *troubleshootv1beta1 return err } - imageName := "replicatedhq/troubleshoot:latest" + imageName := "replicated/troubleshoot:latest" imagePullPolicy := corev1.PullAlways if instance.Spec.Image != "" { diff --git a/pkg/preflight/server.go b/pkg/preflight/server.go index 91dc6d7f..68166793 100644 --- a/pkg/preflight/server.go +++ b/pkg/preflight/server.go @@ -38,7 +38,7 @@ func CreatePreflightServer(client client.Client, scheme *runtime.Scheme, options return nil, nil, err } - imageName := "replicatedhq/troubleshoot:latest" + imageName := "replicated/troubleshoot:latest" imagePullPolicy := corev1.PullAlways if options.ImageName != "" {