func()

in pkg/berglas/bootstrap.go [102:225]


func (c *Client) storageBootstrap(ctx context.Context, i *StorageBootstrapRequest) error {
	projectID := i.ProjectID
	if projectID == "" {
		return fmt.Errorf("missing project ID")
	}

	bucket := i.Bucket
	if bucket == "" {
		return fmt.Errorf("missing bucket name")
	}

	bucketLocation := strings.ToUpper(i.BucketLocation)
	if bucketLocation == "" {
		bucketLocation = "US"
	}

	kmsLocation := i.KMSLocation
	if kmsLocation == "" {
		kmsLocation = "global"
	}

	kmsKeyRing := i.KMSKeyRing
	if kmsKeyRing == "" {
		kmsKeyRing = "berglas"
	}

	kmsCryptoKey := i.KMSCryptoKey
	if kmsCryptoKey == "" {
		kmsCryptoKey = "berglas-key"
	}

	logger := logging.FromContext(ctx).With(
		"project_id", projectID,
		"bucket", bucket,
		"bucket_location", bucketLocation,
		"kms_location", kmsLocation,
		"kms_key_ring", kmsKeyRing,
		"kms_crypto_key", kmsCryptoKey,
	)

	logger.DebugContext(ctx, "bootstrap.start")
	defer logger.DebugContext(ctx, "bootstrap.finish")

	// Create the KMS key ring
	logger.DebugContext(ctx, "creating KMS key ring")

	if _, err := c.kmsClient.CreateKeyRing(ctx, &kmspb.CreateKeyRingRequest{
		Parent: fmt.Sprintf("projects/%s/locations/%s",
			projectID, kmsLocation),
		KeyRingId: kmsKeyRing,
	}); err != nil {
		logger.ErrorContext(ctx, "failed to create KMS key ring", "error", err)

		terr, ok := grpcstatus.FromError(err)
		if !ok || terr.Code() != grpccodes.AlreadyExists {
			return fmt.Errorf("failed to create KMS key ring %s: %w", kmsKeyRing, err)
		}
	}

	// Create the KMS crypto key
	logger.DebugContext(ctx, "creating KMS crypto key")

	rotationPeriod := 30 * 24 * time.Hour
	if _, err := c.kmsClient.CreateCryptoKey(ctx, &kmspb.CreateCryptoKeyRequest{
		Parent: fmt.Sprintf("projects/%s/locations/%s/keyRings/%s",
			projectID, kmsLocation, kmsKeyRing),
		CryptoKeyId: kmsCryptoKey,
		CryptoKey: &kmspb.CryptoKey{
			Purpose: kmspb.CryptoKey_ENCRYPT_DECRYPT,
			RotationSchedule: &kmspb.CryptoKey_RotationPeriod{
				RotationPeriod: &durationpb.Duration{
					Seconds: int64(rotationPeriod.Seconds()),
				},
			},
			NextRotationTime: &timestamppb.Timestamp{
				Seconds: time.Now().Add(time.Duration(rotationPeriod)).Unix(),
			},
			VersionTemplate: &kmspb.CryptoKeyVersionTemplate{
				Algorithm:       kmspb.CryptoKeyVersion_GOOGLE_SYMMETRIC_ENCRYPTION,
				ProtectionLevel: kmspb.ProtectionLevel_SOFTWARE,
			},
		},
	}); err != nil {
		logger.ErrorContext(ctx, "failed to create KMS crypto key", "error", err)

		terr, ok := grpcstatus.FromError(err)
		if !ok || terr.Code() != grpccodes.AlreadyExists {
			return fmt.Errorf("failed to create KMS crypto key %s: %w", kmsCryptoKey, err)
		}
	}

	// Create the storage bucket
	logger.DebugContext(ctx, "creating bucket")

	if err := c.storageClient.Bucket(bucket).Create(ctx, projectID, &storage.BucketAttrs{
		PredefinedACL:              "private",
		PredefinedDefaultObjectACL: "private",
		Location:                   bucketLocation,
		VersioningEnabled:          true,
		Lifecycle: storage.Lifecycle{
			Rules: []storage.LifecycleRule{
				{
					Action: storage.LifecycleAction{
						Type: "Delete",
					},
					Condition: storage.LifecycleCondition{
						NumNewerVersions: 10,
					},
				},
			},
		},
		Labels: map[string]string{
			"purpose": "berglas",
		},
	}); err != nil {
		logger.ErrorContext(ctx, "failed to create bucket", "error", err)

		if !isBucketAlreadyExistsError(err) {
			return fmt.Errorf("failed to create storage bucket %s: %w", bucket, err)
		}
	}

	return nil
}