in pkg/berglas/update.go [199:339]
func (c *Client) storageUpdate(ctx context.Context, i *StorageUpdateRequest) (*Secret, error) {
bucket := i.Bucket
if bucket == "" {
return nil, fmt.Errorf("missing bucket name")
}
object := i.Object
if object == "" {
return nil, fmt.Errorf("missing object name")
}
// Key and Plaintext may be required depending on whether the object exists.
key := i.Key
plaintext := i.Plaintext
generation := i.Generation
metageneration := i.Metageneration
createIfMissing := i.CreateIfMissing
logger := logging.FromContext(ctx).With(
"bucket", bucket,
"object", object,
"key", key,
"generation", generation,
"metageneration", metageneration,
"create_if_missing", createIfMissing,
)
logger.DebugContext(ctx, "update.start")
defer logger.DebugContext(ctx, "update.finish")
// If no specific generations were given, lookup the latest generation to make
// sure we don't conflict with another write.
attrs, err := c.storageClient.
Bucket(bucket).
Object(object).
Attrs(ctx)
if err == nil {
logger = logger.With(
"existing.bucket", attrs.Bucket,
"existing.name", attrs.Name,
"existing.size", attrs.Size,
"existing.metadata", attrs.Metadata,
"existing.generation", attrs.Generation,
"existing.metageneration", attrs.Metageneration,
"existing.created", attrs.Created,
"existing.deleted", attrs.Deleted,
"existing.updated", attrs.Updated,
)
logger.DebugContext(ctx, "found existing storage object")
if generation == 0 {
generation = attrs.Generation
logger = logger.With("generation", generation)
logger.DebugContext(ctx, "setting generation")
}
if metageneration == 0 {
metageneration = attrs.Metageneration
logger = logger.With("metageneration", metageneration)
logger.DebugContext(ctx, "setting metageneration")
}
if key == "" {
key = attrs.Metadata[MetadataKMSKey]
logger = logger.With("key", key)
logger.DebugContext(ctx, "setting key")
}
if plaintext == nil {
logger.DebugContext(ctx, "attempting to access plaintext")
plaintext, err = c.Access(ctx, &AccessRequest{
Bucket: bucket,
Object: object,
Generation: generation,
})
if err != nil {
return nil, fmt.Errorf("failed to get plaintext: %w", err)
}
}
// Get existing IAM policies
logger.DebugContext(ctx, "getting iam policies")
storageHandle := c.storageIAM(bucket, object)
storageP, err := getIAMPolicy(ctx, storageHandle)
if err != nil {
return nil, fmt.Errorf("failed to get IAM policy: %w", err)
}
// Update the secret
logger.DebugContext(ctx, "updating secret")
secret, err := c.encryptAndWrite(ctx, bucket, object, key, plaintext,
generation, metageneration)
if err != nil {
return nil, fmt.Errorf("failed to update secret: %w", err)
}
// Copy over the existing IAM memberships, if any
logger.DebugContext(ctx, "updating iam policies")
if err := updateIAMPolicy(ctx, storageHandle, func(p *iam.Policy) *iam.Policy {
// Copy any IAM permissions from the old object over to the new object.
for _, m := range storageP.Members(iamObjectReader) {
p.Add(m, iamObjectReader)
}
return p
}); err != nil {
return nil, fmt.Errorf("failed to update Storage IAM policy for %s: %w", object, err)
}
return secret, nil
} else if errors.Is(err, storage.ErrObjectNotExist) {
logger.DebugContext(ctx, "secret does not exist")
if !createIfMissing {
return nil, errSecretDoesNotExist
}
if key == "" {
return nil, fmt.Errorf("missing key name")
}
if plaintext == nil {
return nil, fmt.Errorf("missing plaintext")
}
logger.DebugContext(ctx, "creating secret")
// Update the secret.
secret, err := c.encryptAndWrite(ctx, bucket, object, key, plaintext,
generation, metageneration)
if err != nil {
return nil, fmt.Errorf("failed to update secret: %w", err)
}
return secret, nil
} else {
return nil, fmt.Errorf("failed to fetch existing secret: %w", err)
}
}