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 117c6222d1457..5f30bdb613e89 100644 --- a/util/configmanager.go +++ b/util/configmanager.go @@ -4,28 +4,32 @@ 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" ) // 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. @@ -36,42 +40,74 @@ type ConfigManager struct { } // GetSettings retrieves settings from the ConfigManager. -func (mgr *ConfigManager) GetSettings() (settings ArgoCDSettings, err error) { - configMap, err := mgr.readConfigMap(mgr.configMapName) +func (mgr *ConfigManager) GetSettings() (ArgoCDSettings, error) { + settings := ArgoCDSettings{} + settings.LocalUsers = make(map[string]string) + data, err := mgr.getConfigMapData() if err != nil { - return - } - - // Try to retrieve the name of a Kubernetes secret holding root credentials - rootCredentialsSecretName, ok := configMap.Data[RootCredentialsSecretNameKey] - - if !ok { - return + return settings, err } // Try to retrieve the secret - rootCredentials, err := mgr.readSecret(rootCredentialsSecretName) - + rootCredentials, err := mgr.readSecret(data.rootCredentialsSecretName) if err != nil { - return + if errors.IsNotFound(err) { + return settings, nil + } else { + 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] + rootUsername, okUsername := rootCredentials.Data[configManagerRootUsernameKey] + rootPassword, okPassword := rootCredentials.Data[configManagerRootPasswordKey] if okUsername && okPassword { // Store credential info inside LocalUsers settings.LocalUsers[string(rootUsername)] = string(rootPassword) } - return + return settings, nil +} + +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 err + } + + credentials := map[string]string{ + configManagerRootUsernameKey: username, + configManagerRootPasswordKey: passwordHash, + } + + // 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, @@ -80,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{})