github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/cmd/webhook-server/secretmanager/secretmanager.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package secretmanager
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	secretmanager "cloud.google.com/go/secretmanager/apiv1"
    24  	"cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
    25  	"google.golang.org/api/iterator"
    26  	"google.golang.org/genproto/protobuf/field_mask"
    27  )
    28  
    29  // Client is a wrapper of secretmanager client
    30  type Client struct {
    31  	// ProjectID is GCP project in which to store secrets in Secret Manager.
    32  	ProjectID string
    33  	client    *secretmanager.Client
    34  	dryrun    bool
    35  }
    36  
    37  // ClientInterface is the interface for manipulating secretmanager
    38  type ClientInterface interface {
    39  	CreateSecret(ctx context.Context, secretID string) (*secretmanagerpb.Secret, error)
    40  	AddSecretLabel(ctx context.Context, secretID string, labels map[string]string) error
    41  	AddSecretVersion(ctx context.Context, secretName string, payload []byte) error
    42  	ListSecrets(ctx context.Context) ([]*secretmanagerpb.Secret, error)
    43  	GetSecret(ctx context.Context, secretName string) (*secretmanagerpb.Secret, error)
    44  	GetSecretValue(ctx context.Context, secretName, versionName string) ([]byte, error)
    45  }
    46  
    47  // NewClient creates a client for secretmanager, it would fail if not authenticated
    48  func NewClient(projectID string, dryrun bool) (*Client, error) {
    49  	// Create the client.
    50  	ctx := context.Background()
    51  	client, err := secretmanager.NewClient(ctx)
    52  	if err != nil {
    53  		return nil, fmt.Errorf("failed to setup client: %w", err)
    54  	}
    55  	return &Client{ProjectID: projectID, client: client, dryrun: dryrun}, nil
    56  }
    57  
    58  // CreateSecret creates a secret
    59  func (c *Client) CreateSecret(ctx context.Context, secretID string) (*secretmanagerpb.Secret, error) {
    60  	if c.dryrun {
    61  		return nil, nil
    62  	}
    63  	// Create the request to create the secret.
    64  	createSecretReq := &secretmanagerpb.CreateSecretRequest{
    65  		Parent:   fmt.Sprintf("projects/%s", c.ProjectID),
    66  		SecretId: secretID,
    67  		Secret: &secretmanagerpb.Secret{
    68  			Replication: &secretmanagerpb.Replication{
    69  				Replication: &secretmanagerpb.Replication_Automatic_{
    70  					Automatic: &secretmanagerpb.Replication_Automatic{},
    71  				},
    72  			},
    73  		},
    74  	}
    75  
    76  	return c.client.CreateSecret(ctx, createSecretReq)
    77  }
    78  
    79  // AddSecretLabel adds a label to a secret
    80  func (c *Client) AddSecretLabel(ctx context.Context, secretID string, labels map[string]string) error {
    81  	if c.dryrun {
    82  		return nil
    83  	}
    84  	secret, err := c.GetSecret(ctx, secretID)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	existinglabels := secret.Labels
    89  	for key, val := range labels {
    90  		if existinglabels == nil {
    91  			existinglabels = map[string]string{}
    92  		}
    93  		existinglabels[key] = val
    94  	}
    95  	updateSecretReq := &secretmanagerpb.UpdateSecretRequest{
    96  		Secret: &secretmanagerpb.Secret{
    97  			Name:   fmt.Sprintf("projects/%s/secrets/%s", c.ProjectID, secretID),
    98  			Labels: existinglabels,
    99  		},
   100  		UpdateMask: &field_mask.FieldMask{
   101  			Paths: []string{"labels"},
   102  		},
   103  	}
   104  
   105  	_, err = c.client.UpdateSecret(ctx, updateSecretReq)
   106  	return err
   107  }
   108  
   109  // AddSecretVersion adds a secret version, aka update the value of a secret
   110  func (c *Client) AddSecretVersion(ctx context.Context, secretName string, payload []byte) error {
   111  	if c.dryrun {
   112  		return nil
   113  	}
   114  	// Build the request.
   115  	addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{
   116  		Parent: fmt.Sprintf("projects/%s/secrets/%s", c.ProjectID, secretName),
   117  		Payload: &secretmanagerpb.SecretPayload{
   118  			Data: payload,
   119  		},
   120  	}
   121  
   122  	// Call the API.
   123  	_, err := c.client.AddSecretVersion(ctx, addSecretVersionReq)
   124  	return err
   125  }
   126  
   127  // ListSecrets lists all secrets under current project
   128  func (c *Client) ListSecrets(ctx context.Context) ([]*secretmanagerpb.Secret, error) {
   129  	var res []*secretmanagerpb.Secret
   130  	// Build the request.
   131  	listRequest := &secretmanagerpb.ListSecretsRequest{
   132  		Parent: fmt.Sprintf("projects/%s", c.ProjectID),
   133  	}
   134  
   135  	// Call the API.
   136  	it := c.client.ListSecrets(ctx, listRequest)
   137  	for {
   138  		s, err := it.Next()
   139  		if err != nil {
   140  			if err == iterator.Done {
   141  				break
   142  			}
   143  			return nil, err
   144  		}
   145  		res = append(res, s)
   146  	}
   147  	return res, nil
   148  }
   149  
   150  // GetSecret gets secret by name
   151  func (c *Client) GetSecret(ctx context.Context, secretName string) (*secretmanagerpb.Secret, error) {
   152  	// Build the request.
   153  	accessRequest := &secretmanagerpb.GetSecretRequest{
   154  		Name: fmt.Sprintf("projects/%s/secrets/%s", c.ProjectID, secretName),
   155  	}
   156  
   157  	// Call the API.
   158  	return c.client.GetSecret(ctx, accessRequest)
   159  }
   160  
   161  // GetSecretValue gets secret value by its version
   162  func (c *Client) GetSecretValue(ctx context.Context, secretName, versionName string) ([]byte, error) {
   163  	// Build the request.
   164  	accessRequest := &secretmanagerpb.AccessSecretVersionRequest{
   165  		Name: fmt.Sprintf("projects/%s/secrets/%s/versions/%s", c.ProjectID, secretName, versionName),
   166  	}
   167  
   168  	// Call the API.
   169  	result, err := c.client.AccessSecretVersion(ctx, accessRequest)
   170  	if err != nil {
   171  		return nil, fmt.Errorf("failed to access secret version: %w", err)
   172  	}
   173  	return result.Payload.Data, nil
   174  }