Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set up gitconfig extra properties from the specified secret #1375

Closed
wants to merge 1 commit into from
Closed
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
12 changes: 12 additions & 0 deletions pkg/constants/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ const (
// to detect if the user has provided an SSH key with a passhprase.
SSHSecretName = "git-ssh-key"

// DevWorkspaceGitconfigExtraPropertiesSecretName is the name of the secret that is used to define custom gitconfig
// properties that will be mounted to the /etc/gitconfig file. The secret's data map is parsed as a key value structure,
// where each key is a gitconfig section, and value is a property definition. For example, secret's data map is set to
// key:"http", value:"extraHeader = Basic token-content", so the gitconfig file will include next content:
// [http]
// extraHeader = Basic token-content
// The 'git config list' command will reveal the property as: http.extraheader=Basic token-content.
// The secret's data map key must not contain any special characters and whitespaces, and the value must match the
// format: <property> = <value>, otherwise the custom property will be ignored.
// Multiple properties per section are supported, each property set must start from the new line in this case.
DevWorkspaceGitconfigExtraPropertiesSecretName = "devworkspace-gitconfig-extra-properties"

// SSHSecretPassphraseKey is the key used to retrieve the optional passphrase stored inside the SSH secret.
SSHSecretPassphraseKey = "passphrase"

Expand Down
20 changes: 14 additions & 6 deletions pkg/provision/automount/gitconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const mergedGitCredentialsMountPath = "/.git-credentials/"

// ProvisionGitConfiguration takes care of mounting git credentials and a gitconfig into a devworkspace.
func ProvisionGitConfiguration(api sync.ClusterAPI, namespace string) (*Resources, error) {
credentialsSecrets, tlsConfigMaps, err := getGitResources(api, namespace)
credentialsSecrets, extraPropertySecret, tlsConfigMaps, err := getGitResources(api, namespace)
if err != nil {
return nil, err
}
Expand All @@ -48,7 +48,7 @@ func ProvisionGitConfiguration(api sync.ClusterAPI, namespace string) (*Resource
return nil, &dwerrors.FailError{Message: "Failed to collect git credentials secrets", Err: err}
}

gitConfigMap, err := constructGitConfig(namespace, mergedGitCredentialsMountPath, tlsConfigMaps, baseGitConfig)
gitConfigMap, err := constructGitConfig(namespace, mergedGitCredentialsMountPath, tlsConfigMaps, extraPropertySecret, baseGitConfig)
if err != nil {
return nil, &dwerrors.FailError{Message: "Failed to prepare git config for workspace", Err: err}
}
Expand All @@ -69,7 +69,7 @@ func ProvisionGitConfiguration(api sync.ClusterAPI, namespace string) (*Resource
return &resources, nil
}

func getGitResources(api sync.ClusterAPI, namespace string) (credentialSecrets []corev1.Secret, tlsConfigMaps []corev1.ConfigMap, err error) {
func getGitResources(api sync.ClusterAPI, namespace string) (credentialSecrets []corev1.Secret, extraPropertiesSecret *corev1.Secret, tlsConfigMaps []corev1.ConfigMap, err error) {
credentialsLabelSelector := k8sclient.MatchingLabels{
constants.DevWorkspaceGitCredentialLabel: "true",
}
Expand All @@ -79,7 +79,7 @@ func getGitResources(api sync.ClusterAPI, namespace string) (credentialSecrets [

secretList := &corev1.SecretList{}
if err := api.Client.List(api.Ctx, secretList, k8sclient.InNamespace(namespace), credentialsLabelSelector); err != nil {
return nil, nil, err
return nil, nil, nil, err
}
var secrets []corev1.Secret
if len(secretList.Items) > 0 {
Expand All @@ -89,15 +89,23 @@ func getGitResources(api sync.ClusterAPI, namespace string) (credentialSecrets [

configmapList := &corev1.ConfigMapList{}
if err := api.Client.List(api.Ctx, configmapList, k8sclient.InNamespace(namespace), tlsLabelSelector); err != nil {
return nil, nil, err
return nil, nil, nil, err
}
var configmaps []corev1.ConfigMap
if len(configmapList.Items) > 0 {
configmaps = configmapList.Items
}
sortConfigmaps(configmaps)

return secrets, configmaps, nil
secretNN := types.NamespacedName{
Name: constants.DevWorkspaceGitconfigExtraPropertiesSecretName,
Namespace: namespace,
}
secret := &corev1.Secret{}
if err := api.Client.Get(api.Ctx, secretNN, secret); err != nil {
return secrets, nil, configmaps, nil
}
return secrets, secret, configmaps, nil
}

func cleanupGitConfig(api sync.ClusterAPI, namespace string) error {
Expand Down
38 changes: 30 additions & 8 deletions pkg/provision/automount/gitconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func TestOneConfigMapWithNoUserMountPath(t *testing.T) {
buildConfig(defaultName, mountPath, defaultData),
}

gitconfig, err := constructGitConfig(testNamespace, mountPath, configmaps, nil)
gitconfig, err := constructGitConfig(testNamespace, mountPath, configmaps, nil, nil)
if !assert.NoError(t, err, "Should not return error") {
return
}
Expand All @@ -127,7 +127,7 @@ func TestOneConfigMapWithMountPathAndHostAndCert(t *testing.T) {
buildConfig(defaultName, mountPath, defaultData),
}

gitconfig, err := constructGitConfig(testNamespace, mountPath, configmaps, nil)
gitconfig, err := constructGitConfig(testNamespace, mountPath, configmaps, nil, nil)
if !assert.NoError(t, err, "Should not return error") {
return
}
Expand All @@ -140,7 +140,7 @@ func TestOneConfigMapWithMountPathAndWithoutHostAndWithoutCert(t *testing.T) {
buildConfig(defaultName, mountPath, map[string]string{}),
}

_, err := constructGitConfig(testNamespace, mountPath, configmaps, nil)
_, err := constructGitConfig(testNamespace, mountPath, configmaps, nil, nil)
assert.Equal(t, err.Error(), fmt.Sprintf("could not find certificate field in configmap %s", defaultName))
}

Expand All @@ -152,7 +152,7 @@ func TestOneConfigMapWithMountPathAndWithoutHostAndWithCert(t *testing.T) {
}),
}

gitconfig, err := constructGitConfig(testNamespace, mountPath, configmaps, nil)
gitconfig, err := constructGitConfig(testNamespace, mountPath, configmaps, nil, nil)
if !assert.NoError(t, err, "Should not return error") {
return
}
Expand All @@ -167,7 +167,7 @@ func TestOneConfigMapWithMountPathAndWithHostAndWithoutCert(t *testing.T) {
}),
}

_, err := constructGitConfig(testNamespace, mountPath, configmaps, nil)
_, err := constructGitConfig(testNamespace, mountPath, configmaps, nil, nil)
assert.Equal(t, err.Error(), fmt.Sprintf("could not find certificate field in configmap %s", defaultName))
}

Expand All @@ -177,7 +177,7 @@ func TestTwoConfigMapWithNoDefinedMountPathInAnnotation(t *testing.T) {
buildConfig("configmap2", "/folder2", defaultData),
}

gitconfig, err := constructGitConfig(testNamespace, "", configmaps, nil)
gitconfig, err := constructGitConfig(testNamespace, "", configmaps, nil, nil)
if !assert.NoError(t, err, "Should not return error") {
return
}
Expand All @@ -196,7 +196,7 @@ func TestTwoConfigMapWithOneDefaultTLSAndOtherGithubTLS(t *testing.T) {
}),
}

gitconfig, err := constructGitConfig(testNamespace, "", configmaps, nil)
gitconfig, err := constructGitConfig(testNamespace, "", configmaps, nil, nil)
if !assert.NoError(t, err, "Should not return error") {
return
}
Expand All @@ -214,10 +214,32 @@ func TestTwoConfigMapWithBothMissingHost(t *testing.T) {
}),
}

_, err := constructGitConfig(testNamespace, "", configmaps, nil)
_, err := constructGitConfig(testNamespace, "", configmaps, nil, nil)
assert.Equal(t, err.Error(), "multiple git tls credentials do not have host specified")
}

func TestExtraProperties(t *testing.T) {
mountPath := "/sample/test"
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: constants.DevWorkspaceGitconfigExtraPropertiesSecretName,
Namespace: testNamespace,
},
Data: map[string][]byte{
"section": []byte("property1 = value1\nproperty2 = value2\ninvalidPropertySet"),
},
}

gitconfig, err := constructGitConfig(testNamespace, mountPath, []corev1.ConfigMap{}, &secret, nil)
if !assert.NoError(t, err, "Should not return error") {
return
}
assert.Contains(t, gitconfig.Data[gitConfigName], `[section]
property1 = value1
property2 = value2`)
assert.NotContains(t, gitconfig.Data[gitConfigName], `invalidPropertySet`)
}

func buildConfig(name string, mountPath string, data map[string]string) corev1.ConfigMap {
return corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Expand Down
20 changes: 19 additions & 1 deletion pkg/provision/automount/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package automount
import (
"fmt"
"path"
"regexp"
"strings"

"github.com/devfile/devworkspace-operator/pkg/constants"
Expand Down Expand Up @@ -57,7 +58,10 @@ const defaultGitServerTemplate = `[http]
sslCAInfo = %s
`

func constructGitConfig(namespace, credentialMountPath string, certificatesConfigMaps []corev1.ConfigMap, baseGitConfig *string) (*corev1.ConfigMap, error) {
var GitconfigSectionRegexp = regexp.MustCompile(`^[a-zA-Z0-9]*$`)
var GitconfigPropertySetRegexp = regexp.MustCompile(`^.*=.*$`)

func constructGitConfig(namespace, credentialMountPath string, certificatesConfigMaps []corev1.ConfigMap, extraPropertiesSecret *corev1.Secret, baseGitConfig *string) (*corev1.ConfigMap, error) {
var configSettings []string
configSettings = append(configSettings, gitLFSConfig)

Expand Down Expand Up @@ -93,6 +97,20 @@ func constructGitConfig(namespace, credentialMountPath string, certificatesConfi
}
}

if extraPropertiesSecret != nil {
for key, value := range extraPropertiesSecret.Data {
if GitconfigSectionRegexp.MatchString(key) {
configSettings = append(configSettings, fmt.Sprintf(`[%s]`, key))
split := strings.Split(string(value), "\n")
for _, element := range split {
if GitconfigPropertySetRegexp.MatchString(element) {
configSettings = append(configSettings, fmt.Sprintf(` %s`, element))
}
}
}
}
}

gitConfig := strings.Join(configSettings, "\n")

gitConfigMap := &corev1.ConfigMap{
Expand Down
Loading