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

feat:Including import credentials for project via secretRef #138

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
15 changes: 14 additions & 1 deletion apis/projects/v1alpha1/project_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ type ContainerExpirationPolicyAttributes struct {
NameRegex *string `url:"name_regex,omitempty" json:"name_regex,omitempty"`
}

// Struct representing the secret name and the secret namespace to use when importing a project with secret
type ImportUrlSecretRef struct {
Name string `json:"secretName"`
Namespace string `json:"secretNamespace"`
UsernameKey string `json:"username" default:"username"`
PasswordKey string `json:"password" default:"password"`
}

// ProjectParameters define the desired state of a Gitlab Project
type ProjectParameters struct {
// Set whether or not merge requests can be merged with skipped jobs.
Expand Down Expand Up @@ -244,10 +252,15 @@ type ProjectParameters struct {
// +immutable
GroupWithProjectTemplatesID *int `json:"groupWithProjectTemplatesId,omitempty"`

// URL to import repository from.
// URL to import repository from. Provided credentials in the URL will be overwritten in case a valid secretRef is present in ImportUrlSecretRef.
// +optional
// +kubebuilder:validation:MinLength=11
ImportURL *string `json:"importUrl,omitempty"`

// Secret to use when importing project with secret. Provided credentials in ImportUrl will be overwitten with the credentials found in ImportUrlSecretRel.
// +optional
ImportUrlSecretRef *ImportUrlSecretRef `json:"importUrlSecretRef,omitempty"`

// false by default.
// +optional
// +immutable
Expand Down
20 changes: 20 additions & 0 deletions apis/projects/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 24 additions & 2 deletions package/crds/projects.gitlab.crossplane.io_projects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,30 @@ spec:
to be true.
type: integer
importUrl:
description: URL to import repository from.
type: string
description: URL to import repository from. Provided credentials
in the URL will be overwritten in case a valid secretRef is
present in ImportUrlSecretRef.
minLength: 11
type: string
importUrlSecretRef:
description: Secret to use when importing project with secret.
Provided credentials in ImportUrl will be overwitten with the
credentials found in ImportUrlSecretRel.
properties:
password:
type: string
secretName:
type: string
secretNamespace:
type: string
username:
type: string
required:
- password
- secretName
- secretNamespace
- username
Comment on lines +186 to +189
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are username and password required values or should the provider support imports from public repos?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

username and password are required only if importUrlSecretRef is not nil. And yes the provider should also support imports from public repos. @abacus3

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, thank. I think the name importUrlSecretRef´ is a bit misleading. Can we change it to something more explicit like importCredentialsSecretRef`?

type: object
initializeWithReadme:
description: false by default.
type: boolean
Expand Down
60 changes: 60 additions & 0 deletions pkg/controller/projects/projects/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strconv"

"github.com/xanzy/go-gitlab"
"k8s.io/apimachinery/pkg/types"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
Expand All @@ -41,6 +42,10 @@ import (
"github.com/crossplane-contrib/provider-gitlab/pkg/clients"
"github.com/crossplane-contrib/provider-gitlab/pkg/clients/projects"
"github.com/crossplane-contrib/provider-gitlab/pkg/features"

"net/url"

corev1 "k8s.io/api/core/v1"
)

const (
Expand All @@ -50,6 +55,10 @@ const (
errUpdateFailed = "cannot update Gitlab project"
errDeleteFailed = "cannot delete Gitlab project"
errGetFailed = "cannot retrieve Gitlab project with"
errNoImportSecret = "cannot retrieve secret for import"
errUsernameNotFound = "No username in secret for import project"
errPasswordNotFound = "No password in secret for import project"
errParseUrlFailed = "Could not parse the provided importUrl"
)

// SetupProject adds a controller that reconciles Projects.
Expand Down Expand Up @@ -150,6 +159,57 @@ func (e *external) Create(ctx context.Context, mg resource.Managed) (managed.Ext
return managed.ExternalCreation{}, errors.New(errNotProject)
}

keySecretRef := cr.Spec.ForProvider.ImportUrlSecretRef
importUrl := cr.Spec.ForProvider.ImportURL

// User has provided secret for the import
secretRefIsNotEmpty := keySecretRef != nil && keySecretRef.Namespace != "" && keySecretRef.Name != ""
keysAreNotEmpty := keySecretRef != nil && keySecretRef.PasswordKey != "" && keySecretRef.UsernameKey != ""
importUrlIsNotEmpty := importUrl != nil && *importUrl != ""

if secretRefIsNotEmpty && keysAreNotEmpty && importUrlIsNotEmpty {
// Retrieve secret from k8s
namespacedName := types.NamespacedName{
Namespace: keySecretRef.Namespace,
Name: keySecretRef.Name,
}

secret := &corev1.Secret{}

err := e.kube.Get(ctx, namespacedName, secret)

if err != nil {
return managed.ExternalCreation{}, errors.Wrap(err, errNoImportSecret)
}

// Obtain the password from the secret.
password := string(secret.Data[keySecretRef.PasswordKey])
if password == "" {
return managed.ExternalCreation{}, errors.Wrap(err, errPasswordNotFound)
}

// Obtain the username from the secret.
username := string(secret.Data[keySecretRef.UsernameKey])
if username == "" {
return managed.ExternalCreation{}, errors.Wrap(err, errUsernameNotFound)
}

// manipulate url to add the secret. If secret is already in the url, it should be overridden
// https://username:[email protected]
parsedUrl, err := url.Parse(*importUrl)

if err != nil {
return managed.ExternalCreation{}, errors.Wrap(err, errParseUrlFailed)
}

userInfo := url.UserPassword(username, password)

parsedUrl.User = userInfo

// Override importUrl with the manipulated URL containing the credentials found in ImportSecretRef.
*importUrl = parsedUrl.String()
}

prj, _, err := e.client.CreateProject(
projects.GenerateCreateProjectOptions(cr.Name, &cr.Spec.ForProvider),
gitlab.WithContext(ctx),
Expand Down
Loading