Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 6 additions & 51 deletions install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
Expand All @@ -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)
}
}
Expand Down
113 changes: 84 additions & 29 deletions util/configmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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{})
Expand Down