func()

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)
	}
}