Remove Azure location requirement
Instead of requiring the Ark admin to specify a "location" in the azure persistentVolumeProvider config (meaning only a single location is supported), get info about the disk (for its location) when creating a snapshot, and get info about the snapshot (for its location) when creating a disk from a snapshot. Signed-off-by: Andy Goldstein <andy.goldstein@gmail.com>pull/344/head
parent
7c7bfb06b4
commit
331e0c28cc
|
@ -87,7 +87,7 @@ To integrate Ark with Azure, you must create an Ark-specific [service principal]
|
||||||
az group list --query '[].{ ResourceGroup: name, Location:location }'
|
az group list --query '[].{ ResourceGroup: name, Location:location }'
|
||||||
```
|
```
|
||||||
|
|
||||||
Get your cluster's Resource Group name from the `ResourceGroup` value in the response, and use it to set `$AZURE_RESOURCE_GROUP`. (Also note the `Location` value in the response -- this is later used in the Azure-specific portion of the Ark Config).
|
Get your cluster's Resource Group name from the `ResourceGroup` value in the response, and use it to set `$AZURE_RESOURCE_GROUP`.
|
||||||
|
|
||||||
1. Create a service principal with `Contributor` role. This will have subscription-wide access, so protect this credential. You can specify a password or let the `az ad sp create-for-rbac` command create one for you.
|
1. Create a service principal with `Contributor` role. This will have subscription-wide access, so protect this credential. You can specify a password or let the `az ad sp create-for-rbac` command create one for you.
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ Now that you have your Azure credentials stored in a Secret, you need to replace
|
||||||
|
|
||||||
* In file `examples/azure/10-ark-config.yaml`:
|
* In file `examples/azure/10-ark-config.yaml`:
|
||||||
|
|
||||||
* Replace `<YOUR_BUCKET>`, `<YOUR_LOCATION>`, and `<YOUR_TIMEOUT>`. See the [Config definition][8] for details.
|
* Replace `<YOUR_BUCKET>` and `<YOUR_TIMEOUT>`. See the [Config definition][8] for details.
|
||||||
|
|
||||||
Here is an example of a completed file.
|
Here is an example of a completed file.
|
||||||
|
|
||||||
|
@ -142,7 +142,6 @@ metadata:
|
||||||
persistentVolumeProvider:
|
persistentVolumeProvider:
|
||||||
name: azure
|
name: azure
|
||||||
config:
|
config:
|
||||||
location: "West US"
|
|
||||||
apiTimeout: 15m
|
apiTimeout: 15m
|
||||||
backupStorageProvider:
|
backupStorageProvider:
|
||||||
name: azure
|
name: azure
|
||||||
|
@ -153,12 +152,6 @@ scheduleSyncPeriod: 1m
|
||||||
restoreOnlyMode: false
|
restoreOnlyMode: false
|
||||||
```
|
```
|
||||||
|
|
||||||
You can get a complete list of Azure locations with the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
az account list-locations --query "sort([].displayName)" -o tsv
|
|
||||||
```
|
|
||||||
|
|
||||||
## Start the server
|
## Start the server
|
||||||
|
|
||||||
In the root of your Ark directory, run:
|
In the root of your Ark directory, run:
|
||||||
|
|
|
@ -98,17 +98,14 @@ No parameters required.
|
||||||
|
|
||||||
| Key | Type | Default | Meaning |
|
| Key | Type | Default | Meaning |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `location` | string | Required Field | *Example*: "Canada East"<br><br>See [the list of available locations][5] (note that this particular page refers to them as "Regions"). |
|
|
||||||
| `apiTimeout` | metav1.Duration | 2m0s | How long to wait for an Azure API request to complete before timeout. |
|
| `apiTimeout` | metav1.Duration | 2m0s | How long to wait for an Azure API request to complete before timeout. |
|
||||||
|
|
||||||
[0]: #aws
|
[0]: #aws
|
||||||
[1]: #gcp
|
[1]: #gcp
|
||||||
[2]: #azure
|
[2]: #azure
|
||||||
[3]: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions
|
[3]: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions
|
||||||
[5]: https://azure.microsoft.com/en-us/regions/
|
|
||||||
[6]: #parameter-reference
|
[6]: #parameter-reference
|
||||||
[7]: #main-config-parameters
|
[7]: #main-config-parameters
|
||||||
[8]: #overview
|
[8]: #overview
|
||||||
[9]: #example
|
[9]: #example
|
||||||
[10]: http://docs.aws.amazon.com/kms/latest/developerguide/overview.html
|
[10]: http://docs.aws.amazon.com/kms/latest/developerguide/overview.html
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ metadata:
|
||||||
persistentVolumeProvider:
|
persistentVolumeProvider:
|
||||||
name: azure
|
name: azure
|
||||||
config:
|
config:
|
||||||
location: <YOUR_LOCATION>
|
|
||||||
apiTimeout: <YOUR_TIMEOUT>
|
apiTimeout: <YOUR_TIMEOUT>
|
||||||
backupStorageProvider:
|
backupStorageProvider:
|
||||||
name: azure
|
name: azure
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
|
|
||||||
"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/examples/helpers"
|
||||||
"github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions"
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
"github.com/Azure/go-autorest/autorest"
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -45,7 +44,6 @@ const (
|
||||||
azureStorageKeyKey string = "AZURE_STORAGE_KEY"
|
azureStorageKeyKey string = "AZURE_STORAGE_KEY"
|
||||||
azureResourceGroupKey string = "AZURE_RESOURCE_GROUP"
|
azureResourceGroupKey string = "AZURE_RESOURCE_GROUP"
|
||||||
|
|
||||||
locationKey = "location"
|
|
||||||
apiTimeoutKey = "apiTimeout"
|
apiTimeoutKey = "apiTimeout"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,7 +52,6 @@ type blockStore struct {
|
||||||
snaps *disk.SnapshotsClient
|
snaps *disk.SnapshotsClient
|
||||||
subscription string
|
subscription string
|
||||||
resourceGroup string
|
resourceGroup string
|
||||||
location string
|
|
||||||
apiTimeout time.Duration
|
apiTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,16 +79,11 @@ func NewBlockStore() cloudprovider.BlockStore {
|
||||||
|
|
||||||
func (b *blockStore) Init(config map[string]string) error {
|
func (b *blockStore) Init(config map[string]string) error {
|
||||||
var (
|
var (
|
||||||
location = config[locationKey]
|
|
||||||
apiTimeoutVal = config[apiTimeoutKey]
|
apiTimeoutVal = config[apiTimeoutKey]
|
||||||
apiTimeout time.Duration
|
apiTimeout time.Duration
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if location == "" {
|
|
||||||
return errors.Errorf("missing %s in azure configuration", locationKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if apiTimeout, err = time.ParseDuration(apiTimeoutVal); err != nil {
|
if apiTimeout, err = time.ParseDuration(apiTimeoutVal); err != nil {
|
||||||
return errors.Wrapf(err, "could not parse %s (expected time.Duration)", apiTimeoutKey)
|
return errors.Wrapf(err, "could not parse %s (expected time.Duration)", apiTimeoutKey)
|
||||||
}
|
}
|
||||||
|
@ -114,48 +106,28 @@ func (b *blockStore) Init(config map[string]string) error {
|
||||||
disksClient.Authorizer = authorizer
|
disksClient.Authorizer = authorizer
|
||||||
snapsClient.Authorizer = authorizer
|
snapsClient.Authorizer = authorizer
|
||||||
|
|
||||||
// validate the location
|
|
||||||
groupClient := subscriptions.NewGroupClient()
|
|
||||||
groupClient.Authorizer = authorizer
|
|
||||||
|
|
||||||
locs, err := groupClient.ListLocations(cfg[azureSubscriptionIDKey])
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if locs.Value == nil {
|
|
||||||
return 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 errors.Errorf("location %q not found", location)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.disks = &disksClient
|
b.disks = &disksClient
|
||||||
b.snaps = &snapsClient
|
b.snaps = &snapsClient
|
||||||
b.subscription = cfg[azureSubscriptionIDKey]
|
b.subscription = cfg[azureSubscriptionIDKey]
|
||||||
b.resourceGroup = cfg[azureResourceGroupKey]
|
b.resourceGroup = cfg[azureResourceGroupKey]
|
||||||
b.location = location
|
|
||||||
b.apiTimeout = apiTimeout
|
b.apiTimeout = apiTimeout
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *blockStore) CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ string, iops *int64) (string, error) {
|
func (b *blockStore) CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ string, iops *int64) (string, error) {
|
||||||
|
// Lookup snapshot info for its Location
|
||||||
|
snapshotInfo, err := b.snaps.Get(b.resourceGroup, snapshotID)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
fullSnapshotName := getFullSnapshotName(b.subscription, b.resourceGroup, snapshotID)
|
fullSnapshotName := getFullSnapshotName(b.subscription, b.resourceGroup, snapshotID)
|
||||||
diskName := "restore-" + uuid.NewV4().String()
|
diskName := "restore-" + uuid.NewV4().String()
|
||||||
|
|
||||||
disk := disk.Model{
|
disk := disk.Model{
|
||||||
Name: &diskName,
|
Name: &diskName,
|
||||||
Location: &b.location,
|
Location: snapshotInfo.Location,
|
||||||
Properties: &disk.Properties{
|
Properties: &disk.Properties{
|
||||||
CreationData: &disk.CreationData{
|
CreationData: &disk.CreationData{
|
||||||
CreateOption: disk.Copy,
|
CreateOption: disk.Copy,
|
||||||
|
@ -170,7 +142,7 @@ func (b *blockStore) CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ s
|
||||||
|
|
||||||
_, errChan := b.disks.CreateOrUpdate(b.resourceGroup, *disk.Name, disk, ctx.Done())
|
_, errChan := b.disks.CreateOrUpdate(b.resourceGroup, *disk.Name, disk, ctx.Done())
|
||||||
|
|
||||||
err := <-errChan
|
err = <-errChan
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.WithStack(err)
|
return "", errors.WithStack(err)
|
||||||
|
@ -201,6 +173,12 @@ func (b *blockStore) IsVolumeReady(volumeID, volumeAZ string) (ready bool, err e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *blockStore) CreateSnapshot(volumeID, volumeAZ string, tags map[string]string) (string, error) {
|
func (b *blockStore) CreateSnapshot(volumeID, volumeAZ string, tags map[string]string) (string, error) {
|
||||||
|
// Lookup disk info for its Location
|
||||||
|
diskInfo, err := b.disks.Get(b.resourceGroup, volumeID)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
fullDiskName := getFullDiskName(b.subscription, b.resourceGroup, volumeID)
|
fullDiskName := getFullDiskName(b.subscription, b.resourceGroup, volumeID)
|
||||||
// snapshot names must be <= 80 characters long
|
// snapshot names must be <= 80 characters long
|
||||||
var snapshotName string
|
var snapshotName string
|
||||||
|
@ -221,7 +199,7 @@ func (b *blockStore) CreateSnapshot(volumeID, volumeAZ string, tags map[string]s
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Tags: &map[string]*string{},
|
Tags: &map[string]*string{},
|
||||||
Location: &b.location,
|
Location: diskInfo.Location,
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range tags {
|
for k, v := range tags {
|
||||||
|
@ -234,7 +212,7 @@ func (b *blockStore) CreateSnapshot(volumeID, volumeAZ string, tags map[string]s
|
||||||
|
|
||||||
_, errChan := b.snaps.CreateOrUpdate(b.resourceGroup, *snap.Name, snap, ctx.Done())
|
_, errChan := b.snaps.CreateOrUpdate(b.resourceGroup, *snap.Name, snap, ctx.Done())
|
||||||
|
|
||||||
err := <-errChan
|
err = <-errChan
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.WithStack(err)
|
return "", errors.WithStack(err)
|
||||||
|
|
Loading…
Reference in New Issue