diff --git a/pkg/cloudprovider/aws/block_storage_adapter.go b/pkg/cloudprovider/aws/block_storage_adapter.go index f32173225..f57618e2a 100644 --- a/pkg/cloudprovider/aws/block_storage_adapter.go +++ b/pkg/cloudprovider/aws/block_storage_adapter.go @@ -17,8 +17,11 @@ limitations under the License. package aws import ( + "errors" "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" "k8s.io/apimachinery/pkg/util/sets" @@ -33,6 +36,53 @@ type blockStorageAdapter struct { az string } +func getSession(config *aws.Config) (*session.Session, error) { + sess, err := session.NewSession(config) + if err != nil { + return nil, err + } + + if _, err := sess.Config.Credentials.Get(); err != nil { + return nil, err + } + + return sess, nil +} + +func NewBlockStorageAdapter(region, availabilityZone string) (cloudprovider.BlockStorageAdapter, error) { + if region == "" { + return nil, errors.New("missing region in aws configuration in config file") + } + if availabilityZone == "" { + return nil, errors.New("missing availabilityZone in aws configuration in config file") + } + + awsConfig := aws.NewConfig().WithRegion(region) + + sess, err := getSession(awsConfig) + if err != nil { + return nil, err + } + + // validate the availabilityZone + var ( + ec2Client = ec2.New(sess) + azReq = &ec2.DescribeAvailabilityZonesInput{ZoneNames: []*string{&availabilityZone}} + ) + res, err := ec2Client.DescribeAvailabilityZones(azReq) + if err != nil { + return nil, err + } + if len(res.AvailabilityZones) == 0 { + return nil, fmt.Errorf("availability zone %q not found", availabilityZone) + } + + return &blockStorageAdapter{ + ec2: ec2Client, + az: availabilityZone, + }, nil +} + // iopsVolumeTypes is a set of AWS EBS volume types for which IOPS should // be captured during snapshot and provided when creating a new volume // from snapshot. diff --git a/pkg/cloudprovider/aws/object_storage_adapter.go b/pkg/cloudprovider/aws/object_storage_adapter.go index 1171fe7e8..856a32683 100644 --- a/pkg/cloudprovider/aws/object_storage_adapter.go +++ b/pkg/cloudprovider/aws/object_storage_adapter.go @@ -17,9 +17,11 @@ limitations under the License. package aws import ( + "errors" "io" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/service/s3" "github.com/heptio/ark/pkg/cloudprovider" @@ -32,6 +34,40 @@ type objectStorageAdapter struct { kmsKeyID string } +func NewObjectStorageAdapter(region, s3URL, kmsKeyID string, s3ForcePathStyle bool) (cloudprovider.ObjectStorageAdapter, error) { + if region == "" { + return nil, errors.New("missing region in aws configuration in config file") + } + + awsConfig := aws.NewConfig(). + WithRegion(region). + WithS3ForcePathStyle(s3ForcePathStyle) + + if s3URL != "" { + awsConfig = awsConfig.WithEndpointResolver( + endpoints.ResolverFunc(func(service, region string, optFns ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) { + if service == endpoints.S3ServiceID { + return endpoints.ResolvedEndpoint{ + URL: s3URL, + }, nil + } + + return endpoints.DefaultResolver().EndpointFor(service, region, optFns...) + }), + ) + } + + sess, err := getSession(awsConfig) + if err != nil { + return nil, err + } + + return &objectStorageAdapter{ + s3: s3.New(sess), + kmsKeyID: kmsKeyID, + }, nil +} + func (op *objectStorageAdapter) PutObject(bucket string, key string, body io.ReadSeeker) error { req := &s3.PutObjectInput{ Bucket: &bucket, diff --git a/pkg/cloudprovider/aws/storage_adapter.go b/pkg/cloudprovider/aws/storage_adapter.go deleted file mode 100644 index 6e7ab15af..000000000 --- a/pkg/cloudprovider/aws/storage_adapter.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2017 Heptio Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package aws - -import ( - "fmt" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/s3" - - "github.com/heptio/ark/pkg/cloudprovider" -) - -type storageAdapter struct { - blockStorage *blockStorageAdapter - objectStorage *objectStorageAdapter -} - -var _ cloudprovider.StorageAdapter = &storageAdapter{} - -func NewStorageAdapter(config *aws.Config, availabilityZone string, kmsKeyID string) (cloudprovider.StorageAdapter, error) { - sess, err := session.NewSession(config) - if err != nil { - return nil, err - } - - if _, err := sess.Config.Credentials.Get(); err != nil { - return nil, err - } - - // validate the availabilityZone - var ( - ec2Client = ec2.New(sess) - azReq = &ec2.DescribeAvailabilityZonesInput{ZoneNames: []*string{&availabilityZone}} - ) - res, err := ec2Client.DescribeAvailabilityZones(azReq) - if err != nil { - return nil, err - } - if len(res.AvailabilityZones) == 0 { - return nil, fmt.Errorf("availability zone %q not found", availabilityZone) - } - - return &storageAdapter{ - blockStorage: &blockStorageAdapter{ - ec2: ec2Client, - az: availabilityZone, - }, - objectStorage: &objectStorageAdapter{ - s3: s3.New(sess), - kmsKeyID: kmsKeyID, - }, - }, nil -} - -func (op *storageAdapter) ObjectStorage() cloudprovider.ObjectStorageAdapter { - return op.objectStorage -} - -func (op *storageAdapter) BlockStorage() cloudprovider.BlockStorageAdapter { - return op.blockStorage -} diff --git a/pkg/cloudprovider/azure/block_storage_adapter.go b/pkg/cloudprovider/azure/block_storage_adapter.go index 088c0f4de..bc87f9ffc 100644 --- a/pkg/cloudprovider/azure/block_storage_adapter.go +++ b/pkg/cloudprovider/azure/block_storage_adapter.go @@ -20,17 +20,21 @@ import ( "context" "errors" "fmt" + "os" "time" - azure "github.com/Azure/azure-sdk-for-go/arm/disk" + "github.com/Azure/azure-sdk-for-go/arm/disk" + "github.com/Azure/azure-sdk-for-go/arm/examples/helpers" + "github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions" + "github.com/Azure/go-autorest/autorest/azure" "github.com/satori/uuid" "github.com/heptio/ark/pkg/cloudprovider" ) type blockStorageAdapter struct { - disks *azure.DisksClient - snaps *azure.SnapshotsClient + disks *disk.DisksClient + snaps *disk.SnapshotsClient subscription string resourceGroup string location string @@ -39,19 +43,104 @@ type blockStorageAdapter struct { var _ cloudprovider.BlockStorageAdapter = &blockStorageAdapter{} +const ( + azureClientIDKey string = "AZURE_CLIENT_ID" + azureClientSecretKey string = "AZURE_CLIENT_SECRET" + azureSubscriptionIDKey string = "AZURE_SUBSCRIPTION_ID" + azureTenantIDKey string = "AZURE_TENANT_ID" + azureStorageAccountIDKey string = "AZURE_STORAGE_ACCOUNT_ID" + azureStorageKeyKey string = "AZURE_STORAGE_KEY" + azureResourceGroupKey string = "AZURE_RESOURCE_GROUP" +) + +func getConfig() map[string]string { + cfg := map[string]string{ + azureClientIDKey: "", + azureClientSecretKey: "", + azureSubscriptionIDKey: "", + azureTenantIDKey: "", + azureStorageAccountIDKey: "", + azureStorageKeyKey: "", + azureResourceGroupKey: "", + } + + for key := range cfg { + cfg[key] = os.Getenv(key) + } + + return cfg +} + +func NewBlockStorageAdapter(location string, apiTimeout time.Duration) (cloudprovider.BlockStorageAdapter, error) { + if location == "" { + return nil, errors.New("missing location in azure configuration in config file") + } + + if apiTimeout == 0 { + apiTimeout = time.Minute + } + + cfg := getConfig() + + spt, err := helpers.NewServicePrincipalTokenFromCredentials(cfg, azure.PublicCloud.ResourceManagerEndpoint) + if err != nil { + return nil, fmt.Errorf("error creating new service principal: %v", err) + } + + disksClient := disk.NewDisksClient(cfg[azureSubscriptionIDKey]) + snapsClient := disk.NewSnapshotsClient(cfg[azureSubscriptionIDKey]) + + disksClient.Authorizer = spt + snapsClient.Authorizer = spt + + // validate the location + groupClient := subscriptions.NewGroupClient() + groupClient.Authorizer = spt + + locs, err := groupClient.ListLocations(cfg[azureSubscriptionIDKey]) + if err != nil { + return nil, err + } + + if locs.Value == nil { + return nil, errors.New("no locations returned from Azure API") + } + + locationExists := false + for _, loc := range *locs.Value { + if (loc.Name != nil && *loc.Name == location) || (loc.DisplayName != nil && *loc.DisplayName == location) { + locationExists = true + break + } + } + + if !locationExists { + return nil, fmt.Errorf("location %q not found", location) + } + + return &blockStorageAdapter{ + disks: &disksClient, + snaps: &snapsClient, + subscription: cfg[azureSubscriptionIDKey], + resourceGroup: cfg[azureResourceGroupKey], + location: location, + apiTimeout: apiTimeout, + }, nil +} + func (op *blockStorageAdapter) CreateVolumeFromSnapshot(snapshotID, volumeType string, iops *int64) (string, error) { fullSnapshotName := getFullSnapshotName(op.subscription, op.resourceGroup, snapshotID) diskName := "restore-" + uuid.NewV4().String() - disk := azure.Model{ + disk := disk.Model{ Name: &diskName, Location: &op.location, - Properties: &azure.Properties{ - CreationData: &azure.CreationData{ - CreateOption: azure.Copy, + Properties: &disk.Properties{ + CreationData: &disk.CreationData{ + CreateOption: disk.Copy, SourceResourceID: &fullSnapshotName, }, - AccountType: azure.StorageAccountTypes(volumeType), + AccountType: disk.StorageAccountTypes(volumeType), }, } @@ -136,11 +225,11 @@ func (op *blockStorageAdapter) CreateSnapshot(volumeID string, tags map[string]s snapshotName = volumeID[0:80-len(suffix)] + suffix } - snap := azure.Snapshot{ + snap := disk.Snapshot{ Name: &snapshotName, - Properties: &azure.Properties{ - CreationData: &azure.CreationData{ - CreateOption: azure.Copy, + Properties: &disk.Properties{ + CreationData: &disk.CreationData{ + CreateOption: disk.Copy, SourceResourceID: &fullDiskName, }, }, diff --git a/pkg/cloudprovider/azure/object_storage_adapter.go b/pkg/cloudprovider/azure/object_storage_adapter.go index 46600e5b1..e2841c964 100644 --- a/pkg/cloudprovider/azure/object_storage_adapter.go +++ b/pkg/cloudprovider/azure/object_storage_adapter.go @@ -34,6 +34,21 @@ type objectStorageAdapter struct { var _ cloudprovider.ObjectStorageAdapter = &objectStorageAdapter{} +func NewObjectStorageAdapter() (cloudprovider.ObjectStorageAdapter, error) { + cfg := getConfig() + + storageClient, err := storage.NewBasicClient(cfg[azureStorageAccountIDKey], cfg[azureStorageKeyKey]) + if err != nil { + return nil, err + } + + blobClient := storageClient.GetBlobService() + + return &objectStorageAdapter{ + blobClient: &blobClient, + }, nil +} + func (op *objectStorageAdapter) PutObject(bucket string, key string, body io.ReadSeeker) error { container, err := getContainerReference(op.blobClient, bucket) if err != nil { diff --git a/pkg/cloudprovider/azure/storage_adapter.go b/pkg/cloudprovider/azure/storage_adapter.go deleted file mode 100644 index 0023b8267..000000000 --- a/pkg/cloudprovider/azure/storage_adapter.go +++ /dev/null @@ -1,130 +0,0 @@ -/* -Copyright 2017 Heptio Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package azure - -import ( - "errors" - "fmt" - "os" - "time" - - "github.com/Azure/azure-sdk-for-go/arm/disk" - "github.com/Azure/azure-sdk-for-go/arm/examples/helpers" - "github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions" - "github.com/Azure/azure-sdk-for-go/storage" - "github.com/Azure/go-autorest/autorest/azure" - - "github.com/heptio/ark/pkg/cloudprovider" -) - -const ( - azureClientIDKey string = "AZURE_CLIENT_ID" - azureClientSecretKey string = "AZURE_CLIENT_SECRET" - azureSubscriptionIDKey string = "AZURE_SUBSCRIPTION_ID" - azureTenantIDKey string = "AZURE_TENANT_ID" - azureStorageAccountIDKey string = "AZURE_STORAGE_ACCOUNT_ID" - azureStorageKeyKey string = "AZURE_STORAGE_KEY" - azureResourceGroupKey string = "AZURE_RESOURCE_GROUP" -) - -type storageAdapter struct { - objectStorage *objectStorageAdapter - blockStorage *blockStorageAdapter -} - -var _ cloudprovider.StorageAdapter = &storageAdapter{} - -func NewStorageAdapter(location string, apiTimeout time.Duration) (cloudprovider.StorageAdapter, error) { - cfg := map[string]string{ - azureClientIDKey: "", - azureClientSecretKey: "", - azureSubscriptionIDKey: "", - azureTenantIDKey: "", - azureStorageAccountIDKey: "", - azureStorageKeyKey: "", - azureResourceGroupKey: "", - } - - for key := range cfg { - cfg[key] = os.Getenv(key) - } - - spt, err := helpers.NewServicePrincipalTokenFromCredentials(cfg, azure.PublicCloud.ResourceManagerEndpoint) - if err != nil { - return nil, fmt.Errorf("error creating new service principal: %v", err) - } - - disksClient := disk.NewDisksClient(cfg[azureSubscriptionIDKey]) - snapsClient := disk.NewSnapshotsClient(cfg[azureSubscriptionIDKey]) - - disksClient.Authorizer = spt - snapsClient.Authorizer = spt - - storageClient, _ := storage.NewBasicClient(cfg[azureStorageAccountIDKey], cfg[azureStorageKeyKey]) - blobClient := storageClient.GetBlobService() - - if apiTimeout == 0 { - apiTimeout = time.Minute - } - - // validate the location - groupClient := subscriptions.NewGroupClient() - groupClient.Authorizer = spt - - locs, err := groupClient.ListLocations(cfg[azureSubscriptionIDKey]) - if err != nil { - return nil, err - } - - if locs.Value == nil { - return nil, errors.New("no locations returned from Azure API") - } - - locationExists := false - for _, loc := range *locs.Value { - if (loc.Name != nil && *loc.Name == location) || (loc.DisplayName != nil && *loc.DisplayName == location) { - locationExists = true - break - } - } - - if !locationExists { - return nil, fmt.Errorf("location %q not found", location) - } - - return &storageAdapter{ - objectStorage: &objectStorageAdapter{ - blobClient: &blobClient, - }, - blockStorage: &blockStorageAdapter{ - disks: &disksClient, - snaps: &snapsClient, - subscription: cfg[azureSubscriptionIDKey], - resourceGroup: cfg[azureResourceGroupKey], - location: location, - apiTimeout: apiTimeout, - }, - }, nil -} - -func (op *storageAdapter) ObjectStorage() cloudprovider.ObjectStorageAdapter { - return op.objectStorage -} - -func (op *storageAdapter) BlockStorage() cloudprovider.BlockStorageAdapter { - return op.blockStorage -} diff --git a/pkg/cloudprovider/gcp/block_storage_adapter.go b/pkg/cloudprovider/gcp/block_storage_adapter.go index b4170f2b2..8664e476a 100644 --- a/pkg/cloudprovider/gcp/block_storage_adapter.go +++ b/pkg/cloudprovider/gcp/block_storage_adapter.go @@ -17,10 +17,14 @@ limitations under the License. package gcp import ( + "errors" + "fmt" "strings" "time" uuid "github.com/satori/go.uuid" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" "google.golang.org/api/compute/v0.beta" "k8s.io/apimachinery/pkg/util/wait" @@ -36,6 +40,41 @@ type blockStorageAdapter struct { var _ cloudprovider.BlockStorageAdapter = &blockStorageAdapter{} +func NewBlockStorageAdapter(project, zone string) (cloudprovider.BlockStorageAdapter, error) { + if project == "" { + return nil, errors.New("missing project in gcp configuration in config file") + } + if zone == "" { + return nil, errors.New("missing zone in gcp configuration in config file") + } + + client, err := google.DefaultClient(oauth2.NoContext, compute.ComputeScope) + if err != nil { + return nil, err + } + + gce, err := compute.New(client) + if err != nil { + return nil, err + } + + // validate project & zone + res, err := gce.Zones.Get(project, zone).Do() + if err != nil { + return nil, err + } + + if res == nil { + return nil, fmt.Errorf("zone %q not found for project %q", project, zone) + } + + return &blockStorageAdapter{ + gce: gce, + project: project, + zone: zone, + }, nil +} + func (op *blockStorageAdapter) CreateVolumeFromSnapshot(snapshotID string, volumeType string, iops *int64) (volumeID string, err error) { res, err := op.gce.Snapshots.Get(op.project, snapshotID).Do() if err != nil { diff --git a/pkg/cloudprovider/gcp/object_storage_adapter.go b/pkg/cloudprovider/gcp/object_storage_adapter.go index ffd7b1a6e..55ad3b3ae 100644 --- a/pkg/cloudprovider/gcp/object_storage_adapter.go +++ b/pkg/cloudprovider/gcp/object_storage_adapter.go @@ -20,6 +20,8 @@ import ( "io" "strings" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" storage "google.golang.org/api/storage/v1" "github.com/heptio/ark/pkg/cloudprovider" @@ -31,6 +33,22 @@ type objectStorageAdapter struct { var _ cloudprovider.ObjectStorageAdapter = &objectStorageAdapter{} +func NewObjectStorageAdapter() (cloudprovider.ObjectStorageAdapter, error) { + client, err := google.DefaultClient(oauth2.NoContext, storage.DevstorageReadWriteScope) + if err != nil { + return nil, err + } + + gcs, err := storage.New(client) + if err != nil { + return nil, err + } + + return &objectStorageAdapter{ + gcs: gcs, + }, nil +} + func (op *objectStorageAdapter) PutObject(bucket string, key string, body io.ReadSeeker) error { obj := &storage.Object{ Name: key, diff --git a/pkg/cloudprovider/gcp/storage_adapter.go b/pkg/cloudprovider/gcp/storage_adapter.go deleted file mode 100644 index eb9ce642b..000000000 --- a/pkg/cloudprovider/gcp/storage_adapter.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright 2017 Heptio Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package gcp - -import ( - "fmt" - - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" - "google.golang.org/api/compute/v0.beta" - "google.golang.org/api/storage/v1" - - "github.com/heptio/ark/pkg/cloudprovider" -) - -type storageAdapter struct { - blockStorage *blockStorageAdapter - objectStorage *objectStorageAdapter -} - -var _ cloudprovider.StorageAdapter = &storageAdapter{} - -func NewStorageAdapter(project string, zone string) (cloudprovider.StorageAdapter, error) { - client, err := google.DefaultClient(oauth2.NoContext, compute.ComputeScope, storage.DevstorageReadWriteScope) - - if err != nil { - return nil, err - } - - gce, err := compute.New(client) - if err != nil { - return nil, err - } - - // validate project & zone - res, err := gce.Zones.Get(project, zone).Do() - if err != nil { - return nil, err - } - - if res == nil { - return nil, fmt.Errorf("zone %q not found for project %q", project, zone) - } - - gcs, err := storage.New(client) - if err != nil { - return nil, err - } - - return &storageAdapter{ - objectStorage: &objectStorageAdapter{ - gcs: gcs, - }, - blockStorage: &blockStorageAdapter{ - gce: gce, - project: project, - zone: zone, - }, - }, nil -} - -func (op *storageAdapter) ObjectStorage() cloudprovider.ObjectStorageAdapter { - return op.objectStorage -} - -func (op *storageAdapter) BlockStorage() cloudprovider.BlockStorageAdapter { - return op.blockStorage -} diff --git a/pkg/cloudprovider/storage_interfaces.go b/pkg/cloudprovider/storage_interfaces.go index 1f02082ad..699e9c3a1 100644 --- a/pkg/cloudprovider/storage_interfaces.go +++ b/pkg/cloudprovider/storage_interfaces.go @@ -63,10 +63,3 @@ type BlockStorageAdapter interface { // DeleteSnapshot deletes the specified volume snapshot. DeleteSnapshot(snapshotID string) error } - -// StorageAdapter exposes object- and block-storage interfaces and associated methods -// for a given storage provider. -type StorageAdapter interface { - ObjectStorage() ObjectStorageAdapter - BlockStorage() BlockStorageAdapter -} diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 375cb3707..4c5485692 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -18,14 +18,11 @@ package server import ( "context" - "errors" "fmt" "reflect" "sync" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/golang/glog" "github.com/spf13/cobra" @@ -242,11 +239,12 @@ func (s *server) watchConfig(config *api.Config) { func (s *server) initBackupService(config *api.Config) error { glog.Infof("Configuring cloud provider for backup service") - cloud, err := initCloud(config.BackupStorageProvider.CloudProviderConfig, "backupStorageProvider") + objectStorage, err := getObjectStorageProvider(config.BackupStorageProvider.CloudProviderConfig, "backupStorageProvider") if err != nil { return err } - s.backupService = cloudprovider.NewBackupService(cloud.ObjectStorage()) + + s.backupService = cloudprovider.NewBackupService(objectStorage) return nil } @@ -257,102 +255,92 @@ func (s *server) initSnapshotService(config *api.Config) error { } glog.Infof("Configuring cloud provider for snapshot service") - cloud, err := initCloud(*config.PersistentVolumeProvider, "persistentVolumeProvider") + blockStorage, err := getBlockStorageProvider(*config.PersistentVolumeProvider, "persistentVolumeProvider") if err != nil { return err } - s.snapshotService = cloudprovider.NewSnapshotService(cloud.BlockStorage()) + s.snapshotService = cloudprovider.NewSnapshotService(blockStorage) return nil } -func initCloud(config api.CloudProviderConfig, field string) (cloudprovider.StorageAdapter, error) { +func hasOneCloudProvider(cloudConfig api.CloudProviderConfig) bool { + found := false + + if cloudConfig.AWS != nil { + found = true + } + + if cloudConfig.GCP != nil { + if found { + return false + } + found = true + } + + if cloudConfig.Azure != nil { + if found { + return false + } + found = true + } + + return found +} + +func getObjectStorageProvider(cloudConfig api.CloudProviderConfig, field string) (cloudprovider.ObjectStorageAdapter, error) { var ( - cloud cloudprovider.StorageAdapter - err error + objectStorage cloudprovider.ObjectStorageAdapter + err error ) - if config.AWS != nil { - cloud, err = getAWSCloudProvider(config) + if !hasOneCloudProvider(cloudConfig) { + return nil, fmt.Errorf("you must specify exactly one of aws, gcp, or azure for %s", field) } - if config.GCP != nil { - if cloud != nil { - return nil, fmt.Errorf("you may only specify one of aws, gcp, or azure for %s", field) - } - cloud, err = getGCPCloudProvider(config) - } - - if config.Azure != nil { - if cloud != nil { - return nil, fmt.Errorf("you may only specify one of aws, gcp, or azure for %s", field) - } - cloud, err = getAzureCloudProvider(config) + switch { + case cloudConfig.AWS != nil: + objectStorage, err = arkaws.NewObjectStorageAdapter( + cloudConfig.AWS.Region, + cloudConfig.AWS.S3Url, + cloudConfig.AWS.KMSKeyID, + cloudConfig.AWS.S3ForcePathStyle) + case cloudConfig.GCP != nil: + objectStorage, err = gcp.NewObjectStorageAdapter() + case cloudConfig.Azure != nil: + objectStorage, err = azure.NewObjectStorageAdapter() } if err != nil { return nil, err } - if cloud == nil { - return nil, fmt.Errorf("you must specify one of aws, gcp, or azure for %s", field) - } - - return cloud, err + return objectStorage, nil } -func getAWSCloudProvider(cloudConfig api.CloudProviderConfig) (cloudprovider.StorageAdapter, error) { - if cloudConfig.AWS == nil { - return nil, errors.New("missing aws configuration in config file") - } - if cloudConfig.AWS.Region == "" { - return nil, errors.New("missing region in aws configuration in config file") - } - if cloudConfig.AWS.AvailabilityZone == "" { - return nil, errors.New("missing availabilityZone in aws configuration in config file") +func getBlockStorageProvider(cloudConfig api.CloudProviderConfig, field string) (cloudprovider.BlockStorageAdapter, error) { + var ( + blockStorage cloudprovider.BlockStorageAdapter + err error + ) + + if !hasOneCloudProvider(cloudConfig) { + return nil, fmt.Errorf("you must specify exactly one of aws, gcp, or azure for %s", field) } - awsConfig := aws.NewConfig(). - WithRegion(cloudConfig.AWS.Region). - WithS3ForcePathStyle(cloudConfig.AWS.S3ForcePathStyle) - - if cloudConfig.AWS.S3Url != "" { - awsConfig = awsConfig.WithEndpointResolver( - endpoints.ResolverFunc(func(service, region string, optFns ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) { - if service == endpoints.S3ServiceID { - return endpoints.ResolvedEndpoint{ - URL: cloudConfig.AWS.S3Url, - }, nil - } - - return endpoints.DefaultResolver().EndpointFor(service, region, optFns...) - }), - ) + switch { + case cloudConfig.AWS != nil: + blockStorage, err = arkaws.NewBlockStorageAdapter(cloudConfig.AWS.Region, cloudConfig.AWS.AvailabilityZone) + case cloudConfig.GCP != nil: + blockStorage, err = gcp.NewBlockStorageAdapter(cloudConfig.GCP.Project, cloudConfig.GCP.Zone) + case cloudConfig.Azure != nil: + blockStorage, err = azure.NewBlockStorageAdapter(cloudConfig.Azure.Location, cloudConfig.Azure.APITimeout.Duration) } - return arkaws.NewStorageAdapter(awsConfig, cloudConfig.AWS.AvailabilityZone, cloudConfig.AWS.KMSKeyID) -} + if err != nil { + return nil, err + } -func getGCPCloudProvider(cloudConfig api.CloudProviderConfig) (cloudprovider.StorageAdapter, error) { - if cloudConfig.GCP == nil { - return nil, errors.New("missing gcp configuration in config file") - } - if cloudConfig.GCP.Project == "" { - return nil, errors.New("missing project in gcp configuration in config file") - } - if cloudConfig.GCP.Zone == "" { - return nil, errors.New("missing zone in gcp configuration in config file") - } - return gcp.NewStorageAdapter(cloudConfig.GCP.Project, cloudConfig.GCP.Zone) -} - -func getAzureCloudProvider(cloudConfig api.CloudProviderConfig) (cloudprovider.StorageAdapter, error) { - if cloudConfig.Azure == nil { - return nil, errors.New("missing azure configuration in config file") - } - if cloudConfig.Azure.Location == "" { - return nil, errors.New("missing location in azure configuration in config file") - } - return azure.NewStorageAdapter(cloudConfig.Azure.Location, cloudConfig.Azure.APITimeout.Duration) + return blockStorage, nil } func durationMin(a, b time.Duration) time.Duration {