From bd59613cc3f4bd645a280d70ae89410a53765997 Mon Sep 17 00:00:00 2001 From: Alexander Matyushentsev Date: Tue, 27 Mar 2018 11:20:30 -0700 Subject: [PATCH 1/2] Argo server should not fail if configmap name is not provided or config map does not exist --- cmd/argocd-server/commands/root.go | 2 +- util/configmanager.go | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cmd/argocd-server/commands/root.go b/cmd/argocd-server/commands/root.go index c1978c2c850bc..b66f0bbbfcd45 100644 --- a/cmd/argocd-server/commands/root.go +++ b/cmd/argocd-server/commands/root.go @@ -49,7 +49,7 @@ func NewCommand() *cobra.Command { command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path") command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error") command.Flags().StringVar(&repoServerAddress, "repo-server", "localhost:8081", "Repo server address.") - command.Flags().StringVar(&configMapName, "config-map", "", "Name of a Kubernetes config map to use.") + command.Flags().StringVar(&configMapName, "config-map", "argo-cd-cm", "Name of a Kubernetes config map to use.") command.AddCommand(cli.NewVersionCmd(cliName)) return command } diff --git a/util/configmanager.go b/util/configmanager.go index 117c6222d1457..45aae52cce79c 100644 --- a/util/configmanager.go +++ b/util/configmanager.go @@ -4,6 +4,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apiv1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/client-go/kubernetes" ) @@ -36,29 +37,31 @@ type ConfigManager struct { } // GetSettings retrieves settings from the ConfigManager. -func (mgr *ConfigManager) GetSettings() (settings ArgoCDSettings, err error) { +func (mgr *ConfigManager) GetSettings() (ArgoCDSettings, error) { + settings := ArgoCDSettings{} + settings.LocalUsers = make(map[string]string) configMap, err := mgr.readConfigMap(mgr.configMapName) if err != nil { - return + if errors.IsNotFound(err) { + return settings, nil + } else { + return settings, err + } } // Try to retrieve the name of a Kubernetes secret holding root credentials rootCredentialsSecretName, ok := configMap.Data[RootCredentialsSecretNameKey] if !ok { - return + return settings, nil } // Try to retrieve the secret rootCredentials, err := mgr.readSecret(rootCredentialsSecretName) if err != nil { - return + return settings, err } - - // No more errors, so let's populate the struct - settings.LocalUsers = make(map[string]string) - // Retrieve credential info from the secret rootUsername, okUsername := rootCredentials.Data[ConfigManagerRootUsernameKey] rootPassword, okPassword := rootCredentials.Data[ConfigManagerRootPasswordKey] @@ -67,7 +70,7 @@ func (mgr *ConfigManager) GetSettings() (settings ArgoCDSettings, err error) { // Store credential info inside LocalUsers settings.LocalUsers[string(rootUsername)] = string(rootPassword) } - return + return settings, nil } // NewConfigManager generates a new ConfigManager pointer and returns it From ecf6524640cff4e6accc9ac9f9ea091ddcece763 Mon Sep 17 00:00:00 2001 From: Alexander Matyushentsev Date: Tue, 27 Mar 2018 13:25:45 -0700 Subject: [PATCH 2/2] Make sure install and server uses same consistent config-map name --- cmd/argocd-server/commands/root.go | 2 +- install/install.go | 57 ++-------------- util/configmanager.go | 102 ++++++++++++++++++++++------- 3 files changed, 84 insertions(+), 77 deletions(-) diff --git a/cmd/argocd-server/commands/root.go b/cmd/argocd-server/commands/root.go index b66f0bbbfcd45..c1978c2c850bc 100644 --- a/cmd/argocd-server/commands/root.go +++ b/cmd/argocd-server/commands/root.go @@ -49,7 +49,7 @@ func NewCommand() *cobra.Command { command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path") command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error") command.Flags().StringVar(&repoServerAddress, "repo-server", "localhost:8081", "Repo server address.") - command.Flags().StringVar(&configMapName, "config-map", "argo-cd-cm", "Name of a Kubernetes config map to use.") + command.Flags().StringVar(&configMapName, "config-map", "", "Name of a Kubernetes config map to use.") command.AddCommand(cli.NewVersionCmd(cliName)) return command } diff --git a/install/install.go b/install/install.go index 05e0fbeab8549..1ed7e655608c8 100644 --- a/install/install.go +++ b/install/install.go @@ -146,43 +146,6 @@ func (i *Installer) InstallApplicationController() { i.MustInstallResource(kube.MustToUnstructured(&applicationControllerDeployment)) } -// CreateOrUpdateRootCredentials ensures that the named secret contains a given username and password hash. -func (i *Installer) createOrUpdateLocalCredentials(secretName, username, password string) (err error) { - // Used for reading config maps and secrets here - kubeclientset, err := kubernetes.NewForConfig(i.config) - if err != nil { - return - } - - // Don't commit plaintext passwords - passwordHash, err := util.HashPassword(password) - if err != nil { - return - } - - credentials := map[string]string{ - util.ConfigManagerRootUsernameKey: username, - util.ConfigManagerRootPasswordKey: passwordHash, - } - - // See if we've already written this secret - secret, err := kubeclientset.CoreV1().Secrets(i.Namespace).Get(secretName, metav1.GetOptions{}) - if err != nil { - newSecret := &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - }, - } - newSecret.StringData = credentials - _, err = kubeclientset.CoreV1().Secrets(i.Namespace).Create(newSecret) - - } else { - secret.StringData = credentials - _, err = kubeclientset.CoreV1().Secrets(i.Namespace).Update(secret) - } - return -} - func (i *Installer) InstallArgoCDServer() { var argoCDServerServiceAccount apiv1.ServiceAccount var argoCDServerControllerRole rbacv1.Role @@ -199,24 +162,16 @@ func (i *Installer) InstallArgoCDServer() { argoCDServerControllerDeployment.Spec.Template.Spec.Containers[0].Image = i.ServerImage argoCDServerControllerDeployment.Spec.Template.Spec.Containers[0].ImagePullPolicy = apiv1.PullPolicy(i.ImagePullPolicy) - rootCredentialsSecretName := util.ConfigManagerDefaultRootCredentialsSecretName - // Use a Kubernetes ConfigMap, if provided. - if i.InstallOptions.ConfigMap != "" { + kubeclientset, err := kubernetes.NewForConfig(i.config) + errors.CheckError(err) - // Used for reading config maps and secrets here - kubeclientset, err := kubernetes.NewForConfig(i.config) - errors.CheckError(err) + configManager := util.NewConfigManager(kubeclientset, i.Namespace, i.ConfigMap) + errors.CheckError(err) + if i.InstallOptions.ConfigMap != "" { quotedConfigMapName := strconv.Quote(i.InstallOptions.ConfigMap) container := &argoCDServerControllerDeployment.Spec.Template.Spec.Containers[0] container.Command = append(container.Command, "--config-map", quotedConfigMapName) - configMap, err := kubeclientset.CoreV1().ConfigMaps(i.Namespace).Get(i.InstallOptions.ConfigMap, metav1.GetOptions{}) - errors.CheckError(err) - - secretNameOverride, ok := configMap.Data[util.RootCredentialsSecretNameKey] - if ok { - rootCredentialsSecretName = secretNameOverride - } } i.MustInstallResource(kube.MustToUnstructured(&argoCDServerServiceAccount)) @@ -238,7 +193,7 @@ func (i *Installer) InstallArgoCDServer() { errors.CheckError(err) fmt.Print("\n") - err = i.createOrUpdateLocalCredentials(rootCredentialsSecretName, rootUsername, string(rawPassword)) + err = configManager.SetRootUserCredentials(rootUsername, string(rawPassword)) errors.CheckError(err) } } diff --git a/util/configmanager.go b/util/configmanager.go index 45aae52cce79c..5f30bdb613e89 100644 --- a/util/configmanager.go +++ b/util/configmanager.go @@ -10,23 +10,26 @@ import ( // ArgoCDSettings holds in-memory runtime configuration options. type ArgoCDSettings struct { - // LocalUsers holds users local to (stored on) the server. This is to be distinguished from any potential alternative future login providers (LDAP, SAML, etc.) that might ever be added. LocalUsers map[string]string } +type configMapData struct { + rootCredentialsSecretName string +} + const ( - // RootCredentialsSecretNameKey designates the name of the config map field holding the name of a Kubernetes secret. - RootCredentialsSecretNameKey = "rootCredentialsSecretName" + // defaultConfigMapName default name of config map with argo-cd settings + defaultConfigMapName = "argo-cd-cm" - // ConfigManagerDefaultRootCredentialsSecretName holds the default secret name for root credentials. - ConfigManagerDefaultRootCredentialsSecretName = "argocd-root-credentials-secret" + // defaultRootCredentialsSecretName contains default name of secret with root user credentials + defaultRootCredentialsSecretName = "argo-cd-root-credentials" - // ConfigManagerRootUsernameKey designates the root username inside a Kubernetes secret. - ConfigManagerRootUsernameKey = "root.username" + // configManagerRootUsernameKey designates the root username inside a Kubernetes secret. + configManagerRootUsernameKey = "root.username" - // ConfigManagerRootPasswordKey designates the root password inside a Kubernetes secret. - ConfigManagerRootPasswordKey = "root.password" + // configManagerRootPasswordKey designates the root password inside a Kubernetes secret. + configManagerRootPasswordKey = "root.password" ) // ConfigManager holds config info for a new manager with which to access Kubernetes ConfigMaps. @@ -40,7 +43,13 @@ type ConfigManager struct { func (mgr *ConfigManager) GetSettings() (ArgoCDSettings, error) { settings := ArgoCDSettings{} settings.LocalUsers = make(map[string]string) - configMap, err := mgr.readConfigMap(mgr.configMapName) + data, err := mgr.getConfigMapData() + if err != nil { + return settings, err + } + + // Try to retrieve the secret + rootCredentials, err := mgr.readSecret(data.rootCredentialsSecretName) if err != nil { if errors.IsNotFound(err) { return settings, nil @@ -48,33 +57,57 @@ func (mgr *ConfigManager) GetSettings() (ArgoCDSettings, error) { return settings, err } } + // Retrieve credential info from the secret + rootUsername, okUsername := rootCredentials.Data[configManagerRootUsernameKey] + rootPassword, okPassword := rootCredentials.Data[configManagerRootPasswordKey] - // Try to retrieve the name of a Kubernetes secret holding root credentials - rootCredentialsSecretName, ok := configMap.Data[RootCredentialsSecretNameKey] - - if !ok { - return settings, nil + if okUsername && okPassword { + // Store credential info inside LocalUsers + settings.LocalUsers[string(rootUsername)] = string(rootPassword) } + return settings, nil +} - // Try to retrieve the secret - rootCredentials, err := mgr.readSecret(rootCredentialsSecretName) +func (mgr *ConfigManager) SetRootUserCredentials(username string, password string) error { + data, err := mgr.getConfigMapData() + if err != nil { + return err + } + // Don't commit plaintext passwords + passwordHash, err := HashPassword(password) if err != nil { - return settings, err + return err } - // Retrieve credential info from the secret - rootUsername, okUsername := rootCredentials.Data[ConfigManagerRootUsernameKey] - rootPassword, okPassword := rootCredentials.Data[ConfigManagerRootPasswordKey] - if okUsername && okPassword { - // Store credential info inside LocalUsers - settings.LocalUsers[string(rootUsername)] = string(rootPassword) + credentials := map[string]string{ + configManagerRootUsernameKey: username, + configManagerRootPasswordKey: passwordHash, } - return settings, nil + + // See if we've already written this secret + secret, err := mgr.clientset.CoreV1().Secrets(mgr.namespace).Get(data.rootCredentialsSecretName, metav1.GetOptions{}) + if err != nil { + newSecret := &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: data.rootCredentialsSecretName, + }, + } + newSecret.StringData = credentials + _, err = mgr.clientset.CoreV1().Secrets(mgr.namespace).Create(newSecret) + + } else { + secret.StringData = credentials + _, err = mgr.clientset.CoreV1().Secrets(mgr.namespace).Update(secret) + } + return err } // NewConfigManager generates a new ConfigManager pointer and returns it func NewConfigManager(clientset kubernetes.Interface, namespace, configMapName string) (mgr *ConfigManager) { + if configMapName == "" { + configMapName = defaultConfigMapName + } mgr = &ConfigManager{ clientset: clientset, namespace: namespace, @@ -83,6 +116,25 @@ func NewConfigManager(clientset kubernetes.Interface, namespace, configMapName s return } +func (mgr *ConfigManager) getConfigMapData() (configMapData, error) { + data := configMapData{} + configMap, err := mgr.readConfigMap(mgr.configMapName) + if err != nil { + if errors.IsNotFound(err) { + data.rootCredentialsSecretName = defaultRootCredentialsSecretName + return data, nil + } else { + return data, err + } + } + rootCredentialsSecretName, ok := configMap.Data[defaultRootCredentialsSecretName] + if !ok { + rootCredentialsSecretName = defaultRootCredentialsSecretName + } + data.rootCredentialsSecretName = rootCredentialsSecretName + return data, nil +} + // ReadConfigMap retrieves a config map from Kubernetes. func (mgr *ConfigManager) readConfigMap(name string) (configMap *apiv1.ConfigMap, err error) { configMap, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Get(name, metav1.GetOptions{})