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  }