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: allow project scoped generic kubernetes secrets #2975

Merged
merged 25 commits into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
96bf6db
feat: allow project scoped generic kubernetes secrets
Marvin9 Nov 20, 2024
94376ad
Merge remote-tracking branch 'origin' into Marvin9/feat-generic-secrets
Marvin9 Nov 20, 2024
87dc905
feat: update generic secret
Marvin9 Nov 21, 2024
9f0556f
fix: lint
Marvin9 Nov 21, 2024
fa98a71
chore: add tests
Marvin9 Nov 21, 2024
58e7eed
fix: lint
Marvin9 Nov 21, 2024
cad6f83
fix: lint
Marvin9 Nov 21, 2024
166e713
Merge remote-tracking branch 'origin' into Marvin9/feat-generic-secrets
Marvin9 Dec 10, 2024
d513593
chore: update tests
Marvin9 Dec 10, 2024
cbed6c4
Merge remote-tracking branch 'origin' into Marvin9/feat-generic-secrets
Marvin9 Dec 17, 2024
5798f96
chore(ui): address review
Marvin9 Dec 17, 2024
c8dad5b
Merge remote-tracking branch 'origin' into Marvin9/feat-generic-secrets
Marvin9 Dec 18, 2024
67c2047
chore(ui): review updates
Marvin9 Dec 18, 2024
7b537e1
chore(ui): update modal title
Marvin9 Jan 3, 2025
41c4bd1
chore: split endpoints for generic secrets
Marvin9 Jan 7, 2025
0adb936
Merge remote-tracking branch 'origin' into Marvin9/feat-generic-secrets
Marvin9 Jan 7, 2025
1a6b684
chore: updates
Marvin9 Jan 7, 2025
b73e927
chore: reuse secrets endpoint for cred delete
Marvin9 Jan 7, 2025
2faf24f
add tests
Marvin9 Jan 8, 2025
dd5a73f
chore: add tests and sanitize secret fn
Marvin9 Jan 8, 2025
17da553
chore: add tests
Marvin9 Jan 8, 2025
cef1b28
update service.proto
krancour Jan 10, 2025
c965eb4
run codegen
krancour Jan 11, 2025
ad01bb6
endpoint cleanup
krancour Jan 11, 2025
793f29e
small ui tweaks to work with updated api endpoints
krancour Jan 11, 2025
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
3 changes: 3 additions & 0 deletions api/service/v1alpha1/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -536,11 +536,14 @@ message CreateCredentialsRequest {
string project = 1;
string name = 2;
string description = 8;
// type is git, helm, image or generic
string type = 3;
string repo_url = 4 [json_name = "repoURL"];
bool repo_url_is_regex = 5 [json_name = "repoURLIsRegex"];
string username = 6;
string password = 7;
// when type is generic, this field as to create kubernetes Secret resource
map<string, string> data = 9;
}

message CreateCredentialsResponse {
Expand Down
2 changes: 2 additions & 0 deletions api/v1alpha1/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
CredentialTypeLabelValueGit = "git"
CredentialTypeLabelValueHelm = "helm"
CredentialTypeLabelValueImage = "image"
// TODO: Should we explicitly keep this label or agree that absence of this label should implicitly say that credential is generic?

Check failure on line 11 in api/v1alpha1/labels.go

View workflow job for this annotation

GitHub Actions / lint-go

the line is 133 characters long, which exceeds the maximum of 120 characters. (lll)
CredentialTypeLabelValueGeneric = "generic"
Marvin9 marked this conversation as resolved.
Show resolved Hide resolved

// Kargo core API
FreightCollectionLabelKey = "kargo.akuity.io/freight-collection"
Expand Down
144 changes: 127 additions & 17 deletions internal/api/create_credentials_v1alpha1.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
svcv1alpha1 "github.com/akuity/kargo/pkg/api/service/v1alpha1"
)

type credentials struct {
type specificCredentials struct {
krancour marked this conversation as resolved.
Show resolved Hide resolved
project string
name string
credType string
Expand All @@ -25,6 +25,13 @@
description string
}

type genericCredentials struct {
project string
name string
description string
data map[string]string
}

func (s *server) CreateCredentials(
ctx context.Context,
req *connect.Request[svcv1alpha1.CreateCredentialsRequest],
Expand All @@ -34,34 +41,137 @@
return nil, connect.NewError(connect.CodeUnimplemented, errSecretManagementDisabled)
}

creds := credentials{
project: req.Msg.GetProject(),
name: req.Msg.GetName(),
description: req.Msg.GetDescription(),
credType: req.Msg.GetType(),
repoURL: req.Msg.GetRepoUrl(),
repoURLIsRegex: req.Msg.GetRepoUrlIsRegex(),
username: req.Msg.GetUsername(),
password: req.Msg.GetPassword(),
credType := req.Msg.GetType()

if err := validateFieldNotEmpty("type", credType); err != nil {
return nil, err

Check warning on line 47 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L44-L47

Added lines #L44 - L47 were not covered by tests
}

switch credType {
case kargoapi.CredentialTypeLabelValueGit:
case kargoapi.CredentialTypeLabelValueHelm:
case kargoapi.CredentialTypeLabelValueImage:
creds := specificCredentials{
project: req.Msg.GetProject(),
name: req.Msg.GetName(),
description: req.Msg.GetDescription(),
credType: req.Msg.GetType(),
repoURL: req.Msg.GetRepoUrl(),
repoURLIsRegex: req.Msg.GetRepoUrlIsRegex(),
username: req.Msg.GetUsername(),
password: req.Msg.GetPassword(),
}

kubernetesSecret, err := s.createSpecificCredentials(ctx, creds)

if err != nil {
return nil, err
}

Check warning on line 69 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L50-L69

Added lines #L50 - L69 were not covered by tests

return connect.NewResponse(
&svcv1alpha1.CreateCredentialsResponse{
Credentials: kubernetesSecret,
},
), nil
case kargoapi.CredentialTypeLabelValueGeneric:
krancour marked this conversation as resolved.
Show resolved Hide resolved
creds := genericCredentials{
project: req.Msg.GetProject(),
name: req.Msg.GetName(),
data: req.Msg.GetData(),
description: req.Msg.GetDescription(),
}

kubernetesSecret, err := s.createGenericCredentials(ctx, creds)

if err != nil {
return nil, err
}

Check warning on line 88 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L71-L88

Added lines #L71 - L88 were not covered by tests

return connect.NewResponse(&svcv1alpha1.CreateCredentialsResponse{
Credentials: kubernetesSecret,
}), nil

Check warning on line 92 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L90-L92

Added lines #L90 - L92 were not covered by tests
}

return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("type should be one of git, helm, image or generic"))

Check failure on line 95 in internal/api/create_credentials_v1alpha1.go

View workflow job for this annotation

GitHub Actions / lint-go

the line is 124 characters long, which exceeds the maximum of 120 characters. (lll)

Check warning on line 95 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L95

Added line #L95 was not covered by tests
}

func (s *server) createGenericCredentials(ctx context.Context, creds genericCredentials) (*corev1.Secret, error) {
if err := s.validateGenericCredentials(creds); err != nil {
return nil, err
}

Check warning on line 101 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L98-L101

Added lines #L98 - L101 were not covered by tests

kubernetesSecret := s.genericCredentialsToSecret(creds)

if err := s.client.Create(ctx, kubernetesSecret); err != nil {
return nil, fmt.Errorf("create secret: %w", err)
}

Check warning on line 107 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L103-L107

Added lines #L103 - L107 were not covered by tests

// redact
// TODO: current function does not redact some keys, create new function
redactedSecret := sanitizeCredentialSecret(*kubernetesSecret)

return redactedSecret, nil

Check warning on line 113 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L111-L113

Added lines #L111 - L113 were not covered by tests
}

func (s *server) validateGenericCredentials(creds genericCredentials) error {
if err := validateFieldNotEmpty("project", creds.project); err != nil {
return err
}

Check warning on line 119 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L116-L119

Added lines #L116 - L119 were not covered by tests

if err := validateFieldNotEmpty("name", creds.name); err != nil {
return err
}

Check warning on line 123 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L121-L123

Added lines #L121 - L123 were not covered by tests

if len(creds.data) == 0 {
return connect.NewError(connect.CodeInvalidArgument, errors.New("cannot create empty secret"))
}

Check warning on line 127 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L125-L127

Added lines #L125 - L127 were not covered by tests

return nil

Check warning on line 129 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L129

Added line #L129 was not covered by tests
}

func (s *server) genericCredentialsToSecret(creds genericCredentials) *corev1.Secret {
secretsData := map[string][]byte{}

for key, value := range creds.data {
secretsData[key] = []byte(value)
}

Check warning on line 137 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L132-L137

Added lines #L132 - L137 were not covered by tests

kubernetesSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: creds.project,
Name: creds.name,
Labels: map[string]string{
kargoapi.CredentialTypeLabelKey: kargoapi.CredentialTypeLabelValueGeneric,
},
},
Data: secretsData,
}

if creds.description != "" {
kubernetesSecret.Annotations = map[string]string{
kargoapi.AnnotationKeyDescription: creds.description,
}
}

Check warning on line 154 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L139-L154

Added lines #L139 - L154 were not covered by tests

return kubernetesSecret

Check warning on line 156 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L156

Added line #L156 was not covered by tests
}

// creates credentials used for known purpose; specifically external subscriptions to private git repo or helm OCI or docker image

Check failure on line 159 in internal/api/create_credentials_v1alpha1.go

View workflow job for this annotation

GitHub Actions / lint-go

the line is 130 characters long, which exceeds the maximum of 120 characters. (lll)
func (s *server) createSpecificCredentials(ctx context.Context, creds specificCredentials) (*corev1.Secret, error) {

Check warning on line 160 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L160

Added line #L160 was not covered by tests
if err := s.validateCredentials(creds); err != nil {
return nil, err
}

secret := credentialsToSecret(creds)

Check warning on line 166 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L166

Added line #L166 was not covered by tests
if err := s.client.Create(ctx, secret); err != nil {
return nil, fmt.Errorf("create secret: %w", err)
}

return connect.NewResponse(
&svcv1alpha1.CreateCredentialsResponse{
Credentials: sanitizeCredentialSecret(*secret),
},
), nil
return sanitizeCredentialSecret(*secret), nil

Check warning on line 171 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L171

Added line #L171 was not covered by tests
}

func (s *server) validateCredentials(creds credentials) error {
func (s *server) validateCredentials(creds specificCredentials) error {

Check warning on line 174 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L174

Added line #L174 was not covered by tests
if err := validateFieldNotEmpty("project", creds.project); err != nil {
return err
}
Expand Down Expand Up @@ -93,7 +203,7 @@
return validateFieldNotEmpty("password", creds.password)
}

func credentialsToSecret(creds credentials) *corev1.Secret {
func credentialsToSecret(creds specificCredentials) *corev1.Secret {

Check warning on line 206 in internal/api/create_credentials_v1alpha1.go

View check run for this annotation

Codecov / codecov/patch

internal/api/create_credentials_v1alpha1.go#L206

Added line #L206 was not covered by tests
s := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: creds.project,
Expand Down
Loading
Loading