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 }