github.com/argoproj/argo-cd/v3@v3.2.1/cmd/argocd/commands/admin/admin.go (about) 1 package admin 2 3 import ( 4 "context" 5 "reflect" 6 "strings" 7 8 "github.com/spf13/cobra" 9 corev1 "k8s.io/api/core/v1" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 12 "k8s.io/apimachinery/pkg/runtime" 13 "k8s.io/apimachinery/pkg/runtime/schema" 14 "k8s.io/client-go/dynamic" 15 "k8s.io/client-go/rest" 16 "k8s.io/client-go/tools/clientcmd" 17 18 cmdutil "github.com/argoproj/argo-cd/v3/cmd/util" 19 "github.com/argoproj/argo-cd/v3/common" 20 argocdclient "github.com/argoproj/argo-cd/v3/pkg/apiclient" 21 "github.com/argoproj/argo-cd/v3/pkg/apis/application" 22 "github.com/argoproj/argo-cd/v3/util/errors" 23 ) 24 25 const ( 26 // YamlSeparator separates sections of a YAML file 27 yamlSeparator = "---\n" 28 29 applicationsetNamespacesCmdParamsKey = "applicationsetcontroller.namespaces" 30 applicationNamespacesCmdParamsKey = "application.namespaces" 31 ) 32 33 var ( 34 configMapResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"} 35 secretResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"} 36 applicationsResource = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationPlural} 37 appprojectsResource = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.AppProjectPlural} 38 appplicationSetResource = schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: application.ApplicationSetPlural} 39 ) 40 41 type argocdAdditionalNamespaces struct { 42 applicationNamespaces []string 43 applicationsetNamespaces []string 44 } 45 46 type argoCDClientsets struct { 47 configMaps dynamic.ResourceInterface 48 secrets dynamic.ResourceInterface 49 applications dynamic.ResourceInterface 50 projects dynamic.ResourceInterface 51 applicationSets dynamic.ResourceInterface 52 } 53 54 // NewAdminCommand returns a new instance of an argocd command 55 func NewAdminCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { 56 pathOpts := clientcmd.NewDefaultPathOptions() 57 58 command := &cobra.Command{ 59 Use: "admin", 60 Short: "Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access", 61 DisableAutoGenTag: true, 62 Run: func(c *cobra.Command, args []string) { 63 c.HelpFunc()(c, args) 64 }, 65 Example: `# Access the Argo CD web UI 66 $ argocd admin dashboard 67 68 # Reset the initial admin password 69 $ argocd admin initial-password reset 70 `, 71 } 72 73 command.AddCommand(NewClusterCommand(clientOpts, pathOpts)) 74 command.AddCommand(NewProjectsCommand()) 75 command.AddCommand(NewSettingsCommand()) 76 command.AddCommand(NewAppCommand(clientOpts)) 77 command.AddCommand(NewRepoCommand()) 78 command.AddCommand(NewImportCommand()) 79 command.AddCommand(NewExportCommand()) 80 command.AddCommand(NewDashboardCommand(clientOpts)) 81 command.AddCommand(NewNotificationsCommand()) 82 command.AddCommand(NewInitialPasswordCommand()) 83 command.AddCommand(NewRedisInitialPasswordCommand()) 84 85 command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "json", "Set the logging format. One of: json|text") 86 command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error") 87 return command 88 } 89 90 func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientsets { 91 dynamicIf, err := dynamic.NewForConfig(config) 92 errors.CheckError(err) 93 94 return &argoCDClientsets{ 95 configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace), 96 secrets: dynamicIf.Resource(secretResource).Namespace(namespace), 97 applications: dynamicIf.Resource(applicationsResource).Namespace(namespace), 98 projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace), 99 applicationSets: dynamicIf.Resource(appplicationSetResource).Namespace(namespace), 100 } 101 } 102 103 // isArgoCDSecret returns whether or not the given secret is a part of Argo CD configuration 104 // (e.g. argocd-secret, repo credentials, or cluster credentials) 105 func isArgoCDSecret(un unstructured.Unstructured) bool { 106 secretName := un.GetName() 107 if secretName == common.ArgoCDSecretName { 108 return true 109 } 110 if labels := un.GetLabels(); labels != nil { 111 if _, ok := labels[common.LabelKeySecretType]; ok { 112 return true 113 } 114 } 115 if annotations := un.GetAnnotations(); annotations != nil { 116 if annotations[common.AnnotationKeyManagedBy] == common.AnnotationValueManagedByArgoCD { 117 return true 118 } 119 } 120 return false 121 } 122 123 // isArgoCDConfigMap returns true if the configmap name is one of argo cd's well known configmaps 124 func isArgoCDConfigMap(name string) bool { 125 switch name { 126 case common.ArgoCDConfigMapName, common.ArgoCDRBACConfigMapName, common.ArgoCDKnownHostsConfigMapName, common.ArgoCDTLSCertsConfigMapName: 127 return true 128 } 129 return false 130 } 131 132 // specsEqual returns if the spec, data, labels, annotations, and finalizers of the two 133 // supplied objects are equal, indicating that no update is necessary during importing 134 func specsEqual(left, right unstructured.Unstructured) bool { 135 leftAnnotation := left.GetAnnotations() 136 rightAnnotation := right.GetAnnotations() 137 delete(leftAnnotation, corev1.LastAppliedConfigAnnotation) 138 delete(rightAnnotation, corev1.LastAppliedConfigAnnotation) 139 if !reflect.DeepEqual(leftAnnotation, rightAnnotation) { 140 return false 141 } 142 if !reflect.DeepEqual(left.GetLabels(), right.GetLabels()) { 143 return false 144 } 145 if !reflect.DeepEqual(left.GetFinalizers(), right.GetFinalizers()) { 146 return false 147 } 148 switch left.GetKind() { 149 case "Secret", "ConfigMap": 150 leftData, _, _ := unstructured.NestedMap(left.Object, "data") 151 rightData, _, _ := unstructured.NestedMap(right.Object, "data") 152 return reflect.DeepEqual(leftData, rightData) 153 case application.AppProjectKind: 154 leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec") 155 rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec") 156 return reflect.DeepEqual(leftSpec, rightSpec) 157 case application.ApplicationKind: 158 leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec") 159 rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec") 160 leftStatus, _, _ := unstructured.NestedMap(left.Object, "status") 161 rightStatus, _, _ := unstructured.NestedMap(right.Object, "status") 162 // reconciledAt and observedAt are constantly changing and we ignore any diff there 163 delete(leftStatus, "reconciledAt") 164 delete(rightStatus, "reconciledAt") 165 delete(leftStatus, "observedAt") 166 delete(rightStatus, "observedAt") 167 return reflect.DeepEqual(leftSpec, rightSpec) && reflect.DeepEqual(leftStatus, rightStatus) 168 } 169 return false 170 } 171 172 // Get additional namespaces from argocd-cmd-params 173 func getAdditionalNamespaces(ctx context.Context, configMapsClient dynamic.ResourceInterface) *argocdAdditionalNamespaces { 174 applicationNamespaces := make([]string, 0) 175 applicationsetNamespaces := make([]string, 0) 176 177 un, err := configMapsClient.Get(ctx, common.ArgoCDCmdParamsConfigMapName, metav1.GetOptions{}) 178 errors.CheckError(err) 179 var cm corev1.ConfigMap 180 err = runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &cm) 181 errors.CheckError(err) 182 183 namespacesListFromString := func(namespaces string) []string { 184 listOfNamespaces := []string{} 185 186 ss := strings.Split(namespaces, ",") 187 188 for _, namespace := range ss { 189 if namespace != "" { 190 listOfNamespaces = append(listOfNamespaces, strings.TrimSpace(namespace)) 191 } 192 } 193 194 return listOfNamespaces 195 } 196 197 if strNamespaces, ok := cm.Data[applicationNamespacesCmdParamsKey]; ok { 198 applicationNamespaces = namespacesListFromString(strNamespaces) 199 } 200 201 if strNamespaces, ok := cm.Data[applicationsetNamespacesCmdParamsKey]; ok { 202 applicationsetNamespaces = namespacesListFromString(strNamespaces) 203 } 204 205 return &argocdAdditionalNamespaces{ 206 applicationNamespaces: applicationNamespaces, 207 applicationsetNamespaces: applicationsetNamespaces, 208 } 209 }