зеркало из https://github.com/Azure/aks-engine.git
feat: validate VHD availability on azurestack (#2342)
* added image api * images api test * fix: final pr for review * wip including test * tests working * fixed variables in test * fixed tests and code complete * generated templates * changed file formats * added coryright headers * added test validation * reverted kubeconfig for azurestack * Pull request changes * fix validation if version == latest * context timeout * added image api * images api test * fix: final pr for review * wip including test * tests working * fixed variables in test * fixed tests and code complete * generated templates * changed file formats * added coryright headers * added test validation * reverted kubeconfig for azurestack * Pull request changes * fix validation if version == latest * context timeout * Improved code coverage for failure cases Co-authored-by: Javier Darsie <44655727+jadarsie@users.noreply.github.com>
This commit is contained in:
Родитель
4f6e477034
Коммит
d46d6e5207
|
@ -404,6 +404,10 @@ func (dc *deployCmd) run() error {
|
|||
return errors.Wrapf(err, "in SetPropertiesDefaults template %s", dc.apimodelPath)
|
||||
}
|
||||
|
||||
if err = dc.validateOSBaseImage(); err != nil {
|
||||
return errors.Wrapf(err, "validating OS base images required by %s", dc.apimodelPath)
|
||||
}
|
||||
|
||||
template, parameters, err := templateGenerator.GenerateTemplateV2(dc.containerService, engine.DefaultGeneratorCode, BuildTag)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "generating template %s", dc.apimodelPath)
|
||||
|
@ -437,10 +441,11 @@ func (dc *deployCmd) run() error {
|
|||
return err
|
||||
}
|
||||
|
||||
deploymentSuffix := dc.random.Int31()
|
||||
cx, cancel := context.WithTimeout(context.Background(), armhelpers.DefaultARMOperationTimeout)
|
||||
defer cancel()
|
||||
|
||||
deploymentSuffix := dc.random.Int31()
|
||||
|
||||
if res, err := dc.client.DeployTemplate(
|
||||
cx,
|
||||
dc.resourceGroup,
|
||||
|
@ -531,3 +536,15 @@ func (dc *deployCmd) configureContainerMonitoringAddon(ctx context.Context, k8sC
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateOSBaseImage checks if the OS image is available on the target cloud (ATM, Azure Stack only)
|
||||
func (dc *deployCmd) validateOSBaseImage() error {
|
||||
if dc.containerService.Properties.IsAzureStackCloud() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
if err := armhelpers.ValidateRequiredImages(ctx, dc.location, dc.containerService.Properties, dc.client); err != nil {
|
||||
return errors.Wrap(err, "OS base image not available in target cloud")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ type AzureClient struct {
|
|||
disksClient compute.DisksClient
|
||||
availabilitySetsClient compute.AvailabilitySetsClient
|
||||
workspacesClient operationalinsights.WorkspacesClient
|
||||
virtualMachineImagesClient compute.VirtualMachineImagesClient
|
||||
|
||||
applicationsClient graphrbac.ApplicationsClient
|
||||
servicePrincipalsClient graphrbac.ServicePrincipalsClient
|
||||
|
@ -353,6 +354,7 @@ func getClient(env azure.Environment, subscriptionID, tenantID string, armAuthor
|
|||
disksClient: compute.NewDisksClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
|
||||
availabilitySetsClient: compute.NewAvailabilitySetsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
|
||||
workspacesClient: operationalinsights.NewWorkspacesClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
|
||||
virtualMachineImagesClient: compute.NewVirtualMachineImagesClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
|
||||
|
||||
applicationsClient: graphrbac.NewApplicationsClientWithBaseURI(env.GraphEndpoint, tenantID),
|
||||
servicePrincipalsClient: graphrbac.NewServicePrincipalsClientWithBaseURI(env.GraphEndpoint, tenantID),
|
||||
|
@ -373,6 +375,7 @@ func getClient(env azure.Environment, subscriptionID, tenantID string, armAuthor
|
|||
c.disksClient.Authorizer = armAuthorizer
|
||||
c.availabilitySetsClient.Authorizer = armAuthorizer
|
||||
c.workspacesClient.Authorizer = armAuthorizer
|
||||
c.virtualMachineImagesClient.Authorizer = armAuthorizer
|
||||
|
||||
c.deploymentsClient.PollingDelay = time.Second * 5
|
||||
c.resourcesClient.PollingDelay = time.Second * 5
|
||||
|
|
|
@ -66,6 +66,7 @@ type AzureClient struct {
|
|||
disksClient compute.DisksClient
|
||||
availabilitySetsClient compute.AvailabilitySetsClient
|
||||
workspacesClient operationalinsights.WorkspacesClient
|
||||
virtualMachineImagesClient compute.VirtualMachineImagesClient
|
||||
|
||||
applicationsClient graphrbac.ApplicationsClient
|
||||
servicePrincipalsClient graphrbac.ServicePrincipalsClient
|
||||
|
@ -218,6 +219,7 @@ func getClient(env azure.Environment, subscriptionID, tenantID string, armAuthor
|
|||
disksClient: compute.NewDisksClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
|
||||
availabilitySetsClient: compute.NewAvailabilitySetsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
|
||||
workspacesClient: operationalinsights.NewWorkspacesClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
|
||||
virtualMachineImagesClient: compute.NewVirtualMachineImagesClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
|
||||
|
||||
applicationsClient: graphrbac.NewApplicationsClientWithBaseURI(env.GraphEndpoint, tenantID),
|
||||
servicePrincipalsClient: graphrbac.NewServicePrincipalsClientWithBaseURI(env.GraphEndpoint, tenantID),
|
||||
|
@ -238,6 +240,7 @@ func getClient(env azure.Environment, subscriptionID, tenantID string, armAuthor
|
|||
c.disksClient.Authorizer = armAuthorizer
|
||||
c.availabilitySetsClient.Authorizer = armAuthorizer
|
||||
c.workspacesClient.Authorizer = armAuthorizer
|
||||
c.virtualMachineImagesClient.Authorizer = armAuthorizer
|
||||
|
||||
c.deploymentsClient.PollingDelay = time.Second * 5
|
||||
c.resourcesClient.PollingDelay = time.Second * 5
|
||||
|
|
|
@ -37,6 +37,10 @@ const (
|
|||
virutalDiskName = "testVirtualdickName"
|
||||
location = "local"
|
||||
operationID = "7184adda-13fc-4d49-b941-fbbc3b08ed64"
|
||||
publisher = "DefaultPublisher"
|
||||
sku = "DefaultSku"
|
||||
offer = "DefaultOffer"
|
||||
version = "DefaultVersion"
|
||||
filePathTokenResponse = "httpMockClientData/tokenResponse.json"
|
||||
filePathListVirtualMachineScaleSets = "httpMockClientData/listVirtualMachineScaleSets.json"
|
||||
filePathListVirtualMachineScaleSetVMs = "httpMockClientData/listVirtualMachineScaleSetVMs.json"
|
||||
|
@ -49,6 +53,8 @@ const (
|
|||
filePathGetLogAnalyticsWorkspaceSharedKeys = "httpMockClientData/getLogAnalyticsWorkspaceSharedKeys.json"
|
||||
filePathListWorkspacesByResourceGroup = "httpMockClientData/getListWorkspacesByResourceGroup.json"
|
||||
filePathCreateOrUpdateWorkspace = "httpMockClientData/createOrUpdateWorkspace.json"
|
||||
filePathGetVirtualMachineImage = "httpMockClientData/getVirtualMachineImage.json"
|
||||
filePathListVirtualMachineImages = "httpMockClientData/listVirtualMachineImages.json"
|
||||
)
|
||||
|
||||
//HTTPMockClient is an wrapper of httpmock
|
||||
|
@ -76,6 +82,10 @@ type HTTPMockClient struct {
|
|||
Location string
|
||||
OperationID string
|
||||
TokenResponse string
|
||||
Publisher string
|
||||
Sku string
|
||||
Offer string
|
||||
Version string
|
||||
ResponseListVirtualMachineScaleSets string
|
||||
ResponseListVirtualMachineScaleSetVMs string
|
||||
ResponseListVirtualMachines string
|
||||
|
@ -87,6 +97,8 @@ type HTTPMockClient struct {
|
|||
ResponseGetLogAnalyticsWorkspaceSharedKeys string
|
||||
ResponseListWorkspacesByResourceGroup string
|
||||
ResponseCreateOrUpdateWorkspace string
|
||||
ResponseGetVirtualMachineImage string
|
||||
ResponseListVirtualMachineImages string
|
||||
mux *http.ServeMux
|
||||
server *testserver.TestServer
|
||||
}
|
||||
|
@ -132,6 +144,10 @@ func NewHTTPMockClient() (HTTPMockClient, error) {
|
|||
VirutalDiskName: virutalDiskName,
|
||||
Location: location,
|
||||
OperationID: operationID,
|
||||
Publisher: publisher,
|
||||
Offer: offer,
|
||||
Sku: sku,
|
||||
Version: version,
|
||||
mux: http.NewServeMux(),
|
||||
}
|
||||
var err error
|
||||
|
@ -183,6 +199,15 @@ func NewHTTPMockClient() (HTTPMockClient, error) {
|
|||
if err != nil {
|
||||
return client, err
|
||||
}
|
||||
client.ResponseGetVirtualMachineImage, err = readFromFile(filePathGetVirtualMachineImage)
|
||||
if err != nil {
|
||||
return client, err
|
||||
}
|
||||
|
||||
client.ResponseListVirtualMachineImages, err = readFromFile(filePathListVirtualMachineImages)
|
||||
if err != nil {
|
||||
return client, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
@ -541,6 +566,27 @@ func (mc HTTPMockClient) RegisterEnsureDefaultLogAnalyticsWorkspaceCreateNew() {
|
|||
|
||||
}
|
||||
|
||||
// RegisterVMImageFetcherInterface registers the mock response for VMImageFetcherInterface methods.
|
||||
func (mc *HTTPMockClient) RegisterVMImageFetcherInterface() {
|
||||
pattern := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions/%s", mc.SubscriptionID, mc.Location, mc.Publisher, mc.Offer, mc.Sku, mc.Version)
|
||||
mc.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("api-version") != mc.ComputeAPIVersion {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
_, _ = fmt.Fprint(w, mc.ResponseGetVirtualMachineImage)
|
||||
}
|
||||
})
|
||||
|
||||
pattern = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions", mc.SubscriptionID, mc.Location, mc.Publisher, mc.Offer, mc.Sku)
|
||||
mc.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("api-version") != mc.ComputeAPIVersion {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
_, _ = fmt.Fprint(w, mc.ResponseListVirtualMachineImages)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func readFromFile(filePath string) (string, error) {
|
||||
bytes, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"id": "/Subscriptions/14faff93-96b5-428e-8eaa-868d3c7b9397/Providers/Microsoft.Compute/Locations/redmond/Publishers/microsoft-aks/ArtifactTypes/VMImage/Offers/aks/Skus/aks-ubuntu-1604-201910/Versions/2019.10.24",
|
||||
"location": "redmond",
|
||||
"name": "2019.10.24",
|
||||
"properties": {
|
||||
"osDiskImage": {
|
||||
"operatingSystem": "Linux"
|
||||
},
|
||||
"dataDiskImages": []
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
[
|
||||
{
|
||||
"id": "/Subscriptions/14faff93-96b5-428e-8eaa-868d3c7b9397/Providers/Microsoft.Compute/Locations/redmond/Publishers/microsoft-aks/ArtifactTypes/VMImage/Offers/aks/Skus/aks-ubuntu-1604-201910/Versions/2019.10.24",
|
||||
"location": "redmond",
|
||||
"name": "2019.10.24"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package azurestack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
|
||||
)
|
||||
|
||||
// ListVirtualMachineImages returns the list of images available in the current environment
|
||||
func (az *AzureClient) ListVirtualMachineImages(ctx context.Context, location, publisherName, offer, skus string) (compute.ListVirtualMachineImageResource, error) {
|
||||
// random value
|
||||
top := int32(10)
|
||||
list, err := az.virtualMachineImagesClient.List(ctx, location, publisherName, offer, skus, "", &top, "")
|
||||
if err != nil {
|
||||
return compute.ListVirtualMachineImageResource{}, fmt.Errorf("failed to list virtual machine images, %s", err)
|
||||
}
|
||||
r := compute.ListVirtualMachineImageResource{}
|
||||
if err = DeepCopy(&r, list); err != nil {
|
||||
return r, fmt.Errorf("failed to deep copy virtual machine images, %s", err)
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
|
||||
// GetVirtualMachineImage returns an image or an error if the image is not found
|
||||
func (az *AzureClient) GetVirtualMachineImage(ctx context.Context, location, publisherName, offer, skus, version string) (compute.VirtualMachineImage, error) {
|
||||
image, err := az.virtualMachineImagesClient.Get(ctx, location, publisherName, offer, skus, version)
|
||||
if err != nil {
|
||||
return compute.VirtualMachineImage{}, fmt.Errorf("failed to get virtual machine image, %s", err)
|
||||
}
|
||||
r := compute.VirtualMachineImage{}
|
||||
if err = DeepCopy(&r, image); err != nil {
|
||||
return r, fmt.Errorf("failed to deep copy virtual machine image, %s", err)
|
||||
}
|
||||
return r, err
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package azurestack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
badlocation = "badlocation"
|
||||
badPublisher = "badPublisher"
|
||||
badOffer = "badOffer"
|
||||
badSku = "badSku"
|
||||
)
|
||||
|
||||
func TestVMImageFetcherInterface(t *testing.T) {
|
||||
mc, err := NewHTTPMockClient()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create HttpMockClient - %s", err)
|
||||
}
|
||||
mc.RegisterLogin()
|
||||
mc.RegisterVMImageFetcherInterface()
|
||||
|
||||
err = mc.Activate()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to activate HttpMockClient - %s", err)
|
||||
}
|
||||
defer mc.DeactivateAndReset()
|
||||
|
||||
env := mc.GetEnvironment()
|
||||
azureClient, err := NewAzureClientWithClientSecret(env, subscriptionID, "clientID", "secret")
|
||||
if err != nil {
|
||||
t.Fatalf("can not get client %s", err)
|
||||
}
|
||||
|
||||
_, err = azureClient.GetVirtualMachineImage(context.Background(), location, publisher, offer, sku, version)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = azureClient.ListVirtualMachineImages(context.Background(), location, publisher, offer, sku)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMImageFetcherInterfaceBadInput(t *testing.T) {
|
||||
mc, err := NewHTTPMockClient()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create HttpMockClient - %s", err)
|
||||
}
|
||||
mc.RegisterLogin()
|
||||
mc.RegisterVMImageFetcherInterface()
|
||||
|
||||
err = mc.Activate()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to activate HttpMockClient - %s", err)
|
||||
}
|
||||
defer mc.DeactivateAndReset()
|
||||
|
||||
env := mc.GetEnvironment()
|
||||
azureClient, err := NewAzureClientWithClientSecret(env, subscriptionID, "clientID", "secret")
|
||||
if err != nil {
|
||||
t.Fatalf("can not get client %s", err)
|
||||
}
|
||||
|
||||
_, err = azureClient.GetVirtualMachineImage(context.Background(), badlocation, badPublisher, badOffer, badSku, version)
|
||||
if err == nil {
|
||||
t.Fatal("GetVirtualMachineImage did not fail with bad input")
|
||||
}
|
||||
|
||||
_, err = azureClient.ListVirtualMachineImages(context.Background(), badlocation, badPublisher, badOffer, badSku)
|
||||
if err == nil {
|
||||
t.Fatal("ListVirtualMachineImages did not fail with bad input")
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/Azure/aks-engine/pkg/api"
|
||||
"github.com/Azure/aks-engine/pkg/armhelpers/testserver"
|
||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
|
@ -40,6 +41,10 @@ const (
|
|||
virutalDiskName = "testVirtualdickName"
|
||||
location = "local"
|
||||
operationID = "7184adda-13fc-4d49-b941-fbbc3b08ed64"
|
||||
publisher = "DefaultPublisher"
|
||||
sku = "DefaultSku"
|
||||
offer = "DefaultOffer"
|
||||
version = "DefaultVersion"
|
||||
filePathTokenResponse = "httpMockClientData/tokenResponse.json"
|
||||
filePathListVirtualMachineScaleSets = "httpMockClientData/listVirtualMachineScaleSets.json"
|
||||
filePathListVirtualMachineScaleSetVMs = "httpMockClientData/listVirtualMachineScaleSetVMs.json"
|
||||
|
@ -54,6 +59,8 @@ const (
|
|||
filePathCreateOrUpdateWorkspace = "httpMockClientData/createOrUpdateWorkspace.json"
|
||||
filePathListWorkspacesByResourceGroupInMC = "httpMockClientData/getListWorkspacesByResourceGroup.json"
|
||||
filePathCreateOrUpdateWorkspaceInMC = "httpMockClientData/createOrUpdateWorkspace.json"
|
||||
filePathGetVirtualMachineImage = "httpMockClientData/getVirtualMachineImage.json"
|
||||
filePathListVirtualMachineImages = "httpMockClientData/listVirtualMachineImages.json"
|
||||
)
|
||||
|
||||
//HTTPMockClient is an wrapper of httpmock
|
||||
|
@ -84,6 +91,10 @@ type HTTPMockClient struct {
|
|||
Location string
|
||||
OperationID string
|
||||
TokenResponse string
|
||||
Publisher string
|
||||
Sku string
|
||||
Offer string
|
||||
Version string
|
||||
ResponseListVirtualMachineScaleSets string
|
||||
ResponseListVirtualMachineScaleSetVMs string
|
||||
ResponseListVirtualMachines string
|
||||
|
@ -97,6 +108,8 @@ type HTTPMockClient struct {
|
|||
ResponseCreateOrUpdateWorkspace string
|
||||
ResponseListWorkspacesByResourceGroupInMC string
|
||||
ResponseCreateOrUpdateWorkspaceInMC string
|
||||
ResponseGetVirtualMachineImage string
|
||||
ResponseListVirtualMachineImages string
|
||||
mux *http.ServeMux
|
||||
server *testserver.TestServer
|
||||
}
|
||||
|
@ -145,6 +158,10 @@ func NewHTTPMockClient() (HTTPMockClient, error) {
|
|||
VirutalDiskName: virutalDiskName,
|
||||
Location: location,
|
||||
OperationID: operationID,
|
||||
Publisher: publisher,
|
||||
Offer: offer,
|
||||
Sku: sku,
|
||||
Version: version,
|
||||
mux: http.NewServeMux(),
|
||||
}
|
||||
var err error
|
||||
|
@ -206,6 +223,16 @@ func NewHTTPMockClient() (HTTPMockClient, error) {
|
|||
return client, err
|
||||
}
|
||||
|
||||
client.ResponseGetVirtualMachineImage, err = readFromFile(filePathGetVirtualMachineImage)
|
||||
if err != nil {
|
||||
return client, err
|
||||
}
|
||||
|
||||
client.ResponseListVirtualMachineImages, err = readFromFile(filePathListVirtualMachineImages)
|
||||
if err != nil {
|
||||
return client, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
|
@ -594,6 +621,54 @@ func (mc HTTPMockClient) RegisterEnsureDefaultLogAnalyticsWorkspaceCreateNewInMC
|
|||
|
||||
}
|
||||
|
||||
// RegisterVMImageFetcherInterface registers the mock response for VMImageFetcherInterface methods.
|
||||
func (mc *HTTPMockClient) RegisterVMImageFetcherInterface() {
|
||||
pattern := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions/%s", mc.SubscriptionID, mc.Location, mc.Publisher, mc.Offer, mc.Sku, mc.Version)
|
||||
mc.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("api-version") != mc.ComputeAPIVersion {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
_, _ = fmt.Fprint(w, mc.ResponseGetVirtualMachineImage)
|
||||
}
|
||||
})
|
||||
|
||||
pattern = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions/%s", mc.SubscriptionID, mc.Location, api.Ubuntu1604OSImageConfig.ImagePublisher, api.Ubuntu1604OSImageConfig.ImageOffer, api.Ubuntu1604OSImageConfig.ImageSku, api.Ubuntu1604OSImageConfig.ImageVersion)
|
||||
mc.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("api-version") != mc.ComputeAPIVersion {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
_, _ = fmt.Fprint(w, mc.ResponseGetVirtualMachineImage)
|
||||
}
|
||||
})
|
||||
|
||||
pattern = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions/%s", mc.SubscriptionID, mc.Location, api.WindowsServer2019OSImageConfig.ImagePublisher, api.WindowsServer2019OSImageConfig.ImageOffer, api.WindowsServer2019OSImageConfig.ImageSku, api.WindowsServer2019OSImageConfig.ImageVersion)
|
||||
mc.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("api-version") != mc.ComputeAPIVersion {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
_, _ = fmt.Fprint(w, mc.ResponseGetVirtualMachineImage)
|
||||
}
|
||||
})
|
||||
|
||||
pattern = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions/%s", mc.SubscriptionID, mc.Location, api.AKSUbuntu1604OSImageConfig.ImagePublisher, api.AKSUbuntu1604OSImageConfig.ImageOffer, api.AKSUbuntu1604OSImageConfig.ImageSku, api.AKSUbuntu1604OSImageConfig.ImageVersion)
|
||||
mc.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("api-version") != mc.ComputeAPIVersion {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
_, _ = fmt.Fprint(w, mc.ResponseGetVirtualMachineImage)
|
||||
}
|
||||
})
|
||||
|
||||
pattern = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions", mc.SubscriptionID, mc.Location, mc.Publisher, mc.Offer, mc.Sku)
|
||||
mc.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("api-version") != mc.ComputeAPIVersion {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
_, _ = fmt.Fprint(w, mc.ResponseListVirtualMachineImages)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func readFromFile(filePath string) (string, error) {
|
||||
bytes, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"id": "/Subscriptions/14faff93-96b5-428e-8eaa-868d3c7b9397/Providers/Microsoft.Compute/Locations/redmond/Publishers/microsoft-aks/ArtifactTypes/VMImage/Offers/aks/Skus/aks-ubuntu-1604-201910/Versions/2019.10.24",
|
||||
"location": "redmond",
|
||||
"name": "2019.10.24",
|
||||
"properties": {
|
||||
"osDiskImage": {
|
||||
"operatingSystem": "Linux"
|
||||
},
|
||||
"dataDiskImages": []
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
[
|
||||
{
|
||||
"id": "/Subscriptions/14faff93-96b5-428e-8eaa-868d3c7b9397/Providers/Microsoft.Compute/Locations/redmond/Publishers/microsoft-aks/ArtifactTypes/VMImage/Offers/aks/Skus/aks-ubuntu-1604-201910/Versions/2019.10.24",
|
||||
"location": "redmond",
|
||||
"name": "2019.10.24"
|
||||
}
|
||||
]
|
|
@ -79,6 +79,16 @@ type DiskListPage interface {
|
|||
Values() []compute.Disk
|
||||
}
|
||||
|
||||
//VMImageFetcher is an extension of AKSEngine client allows us to operate on the virtual machine images in the environment
|
||||
type VMImageFetcher interface {
|
||||
|
||||
// ListVirtualMachineImages return a list of images
|
||||
ListVirtualMachineImages(ctx context.Context, location, publisherName, offer, skus string) (compute.ListVirtualMachineImageResource, error)
|
||||
|
||||
// GetVirtualMachineImage return a virtual machine image
|
||||
GetVirtualMachineImage(ctx context.Context, location, publisherName, offer, skus, version string) (compute.VirtualMachineImage, error)
|
||||
}
|
||||
|
||||
// AKSEngineClient is the interface used to talk to an Azure environment.
|
||||
// This interface exposes just the subset of Azure APIs and clients needed for
|
||||
// AKS Engine.
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package armhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Azure/aks-engine/pkg/api"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type validationResult struct {
|
||||
image api.AzureOSImageConfig
|
||||
errorData error
|
||||
}
|
||||
|
||||
// ValidateRequiredImages checks that the OS images required by both
|
||||
// master and agent pools are available on the target cloud
|
||||
func ValidateRequiredImages(ctx context.Context, location string, p *api.Properties, client AKSEngineClient) error {
|
||||
if fetcher, ok := client.(VMImageFetcher); ok {
|
||||
missingImages := make(map[api.Distro]validationResult)
|
||||
for distro, i := range requiredImages(p) {
|
||||
if i.ImageVersion == "latest" {
|
||||
list, err := fetcher.ListVirtualMachineImages(ctx, location, i.ImagePublisher, i.ImageOffer, i.ImageSku)
|
||||
if err != nil || len(*list.Value) == 0 {
|
||||
missingImages[distro] = validationResult{
|
||||
image: i,
|
||||
errorData: err,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, err := fetcher.GetVirtualMachineImage(ctx, location, i.ImagePublisher, i.ImageOffer, i.ImageSku, i.ImageVersion); err != nil {
|
||||
missingImages[distro] = validationResult{
|
||||
image: i,
|
||||
errorData: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(missingImages) == 0 {
|
||||
return nil
|
||||
}
|
||||
return printErrorIfAny(missingImages)
|
||||
}
|
||||
return errors.New("parameter client is not a VMImageFetcher")
|
||||
}
|
||||
|
||||
func requiredImages(p *api.Properties) map[api.Distro]api.AzureOSImageConfig {
|
||||
images := make(map[api.Distro]api.AzureOSImageConfig)
|
||||
images[p.MasterProfile.Distro] = toImageConfig(p.MasterProfile.Distro)
|
||||
for _, app := range p.AgentPoolProfiles {
|
||||
if app.OSType == api.Windows {
|
||||
images[app.Distro] = api.WindowsServer2019OSImageConfig
|
||||
} else {
|
||||
images[app.Distro] = toImageConfig(app.Distro)
|
||||
}
|
||||
}
|
||||
return images
|
||||
}
|
||||
|
||||
func printErrorIfAny(missingImages map[api.Distro]validationResult) error {
|
||||
for _, value := range missingImages {
|
||||
i := value.image
|
||||
log.Errorf("error: %+v", value.errorData)
|
||||
log.Errorf("Image Publisher: %s, Offer: %s, SKU: %s, Version: %s", i.ImagePublisher, i.ImageOffer, i.ImageSku, i.ImageVersion)
|
||||
}
|
||||
return errors.New("some VM images are missing on the target cloud")
|
||||
}
|
||||
|
||||
func toImageConfig(distro api.Distro) api.AzureOSImageConfig {
|
||||
if distro == "" {
|
||||
return api.Ubuntu1604OSImageConfig
|
||||
}
|
||||
switch distro {
|
||||
case api.Ubuntu:
|
||||
return api.Ubuntu1604OSImageConfig
|
||||
case api.Ubuntu1804:
|
||||
return api.Ubuntu1804OSImageConfig
|
||||
case api.RHEL:
|
||||
return api.RHELOSImageConfig
|
||||
case api.CoreOS:
|
||||
return api.CoreOSImageConfig
|
||||
case api.AKSUbuntu1604:
|
||||
return api.AKSUbuntu1604OSImageConfig
|
||||
case api.AKSUbuntu1804:
|
||||
return api.AKSUbuntu1804OSImageConfig
|
||||
case api.ACC1604:
|
||||
return api.ACC1604OSImageConfig
|
||||
default:
|
||||
return api.Ubuntu1604OSImageConfig
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package armhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/aks-engine/pkg/api"
|
||||
)
|
||||
|
||||
func TestValidateRequiredImages(t *testing.T) {
|
||||
mc, err := NewHTTPMockClient()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create HttpMockClient - %s", err)
|
||||
}
|
||||
|
||||
mc.RegisterLogin()
|
||||
mc.RegisterVMImageFetcherInterface()
|
||||
|
||||
err = mc.Activate()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to activate HttpMockClient - %s", err)
|
||||
}
|
||||
defer mc.DeactivateAndReset()
|
||||
|
||||
env := mc.GetEnvironment()
|
||||
azureClient, err := NewAzureClientWithClientSecret(env, subscriptionID, "clientID", "secret")
|
||||
if err != nil {
|
||||
t.Fatalf("can not get client %s", err)
|
||||
}
|
||||
|
||||
testProperties := api.Properties{}
|
||||
masterProfile := api.MasterProfile{}
|
||||
masterProfile.Distro = api.AKSUbuntu1604
|
||||
|
||||
profile := api.AgentPoolProfile{
|
||||
OSType: api.Linux,
|
||||
Distro: api.AKSUbuntu1604,
|
||||
}
|
||||
|
||||
winprofile := api.AgentPoolProfile{
|
||||
OSType: api.Windows,
|
||||
}
|
||||
|
||||
agentProfiles := []*api.AgentPoolProfile{}
|
||||
agentProfiles = append(agentProfiles, &profile)
|
||||
agentProfiles = append(agentProfiles, &winprofile)
|
||||
|
||||
testProperties.AgentPoolProfiles = agentProfiles
|
||||
testProperties.MasterProfile = &masterProfile
|
||||
|
||||
if err := ValidateRequiredImages(context.Background(), location, &testProperties, azureClient); err != nil {
|
||||
t.Fatalf("can not validate required images %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRequiredImagesMissingImageCase(t *testing.T) {
|
||||
|
||||
mc, err := NewHTTPMockClient()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create HttpMockClient - %s", err)
|
||||
}
|
||||
|
||||
mc.RegisterLogin()
|
||||
mc.RegisterVMImageFetcherInterface()
|
||||
|
||||
err = mc.Activate()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to activate HttpMockClient - %s", err)
|
||||
}
|
||||
defer mc.DeactivateAndReset()
|
||||
|
||||
env := mc.GetEnvironment()
|
||||
azureClient, err := NewAzureClientWithClientSecret(env, subscriptionID, "clientID", "secret")
|
||||
if err != nil {
|
||||
t.Fatalf("can not get client %s", err)
|
||||
}
|
||||
|
||||
testProperties := api.Properties{}
|
||||
masterProfile := api.MasterProfile{}
|
||||
|
||||
masterProfile.Distro = api.AKSUbuntu1804
|
||||
|
||||
profile := api.AgentPoolProfile{
|
||||
OSType: api.Linux,
|
||||
Distro: api.AKSUbuntu1804,
|
||||
}
|
||||
|
||||
agentProfiles := []*api.AgentPoolProfile{}
|
||||
agentProfiles = append(agentProfiles, &profile)
|
||||
|
||||
testProperties.AgentPoolProfiles = agentProfiles
|
||||
testProperties.MasterProfile = &masterProfile
|
||||
|
||||
if err := ValidateRequiredImages(context.Background(), location, &testProperties, azureClient); err == nil {
|
||||
t.Fatal("could not fail fast for missing images")
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package armhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
|
||||
)
|
||||
|
||||
// ListVirtualMachineImages returns the list of images available in the current environment
|
||||
func (az *AzureClient) ListVirtualMachineImages(ctx context.Context, location, publisherName, offer, skus string) (compute.ListVirtualMachineImageResource, error) {
|
||||
// random value
|
||||
top := int32(10)
|
||||
list, err := az.virtualMachineImagesClient.List(ctx, location, publisherName, offer, skus, "", &top, "")
|
||||
return list, err
|
||||
}
|
||||
|
||||
// GetVirtualMachineImage returns an image or an error where there is no image
|
||||
func (az *AzureClient) GetVirtualMachineImage(ctx context.Context, location, publisherName, offer, skus, version string) (compute.VirtualMachineImage, error) {
|
||||
image, err := az.virtualMachineImagesClient.Get(ctx, location, publisherName, offer, skus, version)
|
||||
return image, err
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package armhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
badlocation = "badlocation"
|
||||
badPublisher = "badPublisher"
|
||||
badOffer = "badOffer"
|
||||
badSku = "badSku"
|
||||
)
|
||||
|
||||
func TestVMImageFetcherInterface(t *testing.T) {
|
||||
mc, err := NewHTTPMockClient()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create HttpMockClient - %s", err)
|
||||
}
|
||||
mc.RegisterLogin()
|
||||
mc.RegisterVMImageFetcherInterface()
|
||||
|
||||
err = mc.Activate()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to activate HttpMockClient - %s", err)
|
||||
}
|
||||
defer mc.DeactivateAndReset()
|
||||
|
||||
env := mc.GetEnvironment()
|
||||
azureClient, err := NewAzureClientWithClientSecret(env, subscriptionID, "clientID", "secret")
|
||||
if err != nil {
|
||||
t.Fatalf("can not get client %s", err)
|
||||
}
|
||||
|
||||
_, err = azureClient.GetVirtualMachineImage(context.Background(), location, publisher, offer, sku, version)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = azureClient.ListVirtualMachineImages(context.Background(), location, publisher, offer, sku)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMImageFetcherInterfaceBadInput(t *testing.T) {
|
||||
mc, err := NewHTTPMockClient()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create HttpMockClient - %s", err)
|
||||
}
|
||||
mc.RegisterLogin()
|
||||
mc.RegisterVMImageFetcherInterface()
|
||||
|
||||
err = mc.Activate()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to activate HttpMockClient - %s", err)
|
||||
}
|
||||
defer mc.DeactivateAndReset()
|
||||
|
||||
env := mc.GetEnvironment()
|
||||
azureClient, err := NewAzureClientWithClientSecret(env, subscriptionID, "clientID", "secret")
|
||||
if err != nil {
|
||||
t.Fatalf("can not get client %s", err)
|
||||
}
|
||||
|
||||
_, err = azureClient.GetVirtualMachineImage(context.Background(), badlocation, badPublisher, badOffer, badSku, version)
|
||||
if err == nil {
|
||||
t.Fatal("GetVirtualMachineImage did not fail with bad input")
|
||||
}
|
||||
|
||||
_, err = azureClient.ListVirtualMachineImages(context.Background(), badlocation, badPublisher, badOffer, badSku)
|
||||
if err == nil {
|
||||
t.Fatal("ListVirtualMachineImages did not fail with bad input")
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче