Add more tests

This commit is contained in:
Paul Meyer 2020-04-08 21:05:26 +00:00
parent b12aedcda9
commit 4ffe5611b8
8 changed files with 652 additions and 10 deletions

View File

@ -1,5 +1,6 @@
package chroot package chroot
const ( const (
stateBagKey_OSDiskResourceID = "os_disk_resource_id" stateBagKey_OSDiskResourceID = "os_disk_resource_id"
stateBagKey_OSDiskSnapshotResourceID = "os_disk_snapshot_resource_id"
) )

View File

@ -0,0 +1,133 @@
package chroot
import (
"reflect"
"strings"
"testing"
)
func TestSharedImageGalleryDestination_ResourceID(t *testing.T) {
sigd := SharedImageGalleryDestination{
ResourceGroup: "ResourceGroup",
GalleryName: "GalleryName",
ImageName: "ImageName",
ImageVersion: "ImageVersion",
}
want := "/subscriptions/SubscriptionID/resourceGroups/ResourceGroup/providers/Microsoft.Compute/galleries/GalleryName/images/ImageName/versions/ImageVersion"
if got := sigd.ResourceID("SubscriptionID"); !strings.EqualFold(got, want) {
t.Errorf("SharedImageGalleryDestination.ResourceID() = %v, want %v", got, want)
}
}
func TestSharedImageGalleryDestination_Validate(t *testing.T) {
type fields struct {
ResourceGroup string
GalleryName string
ImageName string
ImageVersion string
TargetRegions []TargetRegion
ExcludeFromLatest bool
}
tests := []struct {
name string
fields fields
wantErrs []string
wantWarns []string
}{
{
name: "complete",
fields: fields{
ResourceGroup: "ResourceGroup",
GalleryName: "GalleryName",
ImageName: "ImageName",
ImageVersion: "0.1.2",
TargetRegions: []TargetRegion{
TargetRegion{
Name: "region1",
ReplicaCount: 5,
StorageAccountType: "Standard_ZRS",
},
TargetRegion{
Name: "region2",
ReplicaCount: 3,
StorageAccountType: "Standard_LRS",
},
},
ExcludeFromLatest: true,
},
},
{
name: "warn if target regions not specified",
fields: fields{
ResourceGroup: "ResourceGroup",
GalleryName: "GalleryName",
ImageName: "ImageName",
ImageVersion: "0.1.2",
},
wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"},
},
{
name: "version format",
wantErrs: []string{
"sigdest.image_version should match '^[0-9]+\\.[0-9]+\\.[0-9]+$'",
},
fields: fields{
ResourceGroup: "ResourceGroup",
GalleryName: "GalleryName",
ImageName: "ImageName",
ImageVersion: "0.1.2alpha",
TargetRegions: []TargetRegion{
TargetRegion{
Name: "region1",
ReplicaCount: 5,
StorageAccountType: "Standard_ZRS",
},
TargetRegion{
Name: "region2",
ReplicaCount: 3,
StorageAccountType: "Standard_LRS",
},
},
ExcludeFromLatest: true,
},
},
{
name: "required fields",
wantErrs: []string{
"sigdest.resource_group is required",
"sigdest.gallery_name is required",
"sigdest.image_name is required",
"sigdest.image_version should match '^[0-9]+\\.[0-9]+\\.[0-9]+$'",
},
wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sigd := &SharedImageGalleryDestination{
ResourceGroup: tt.fields.ResourceGroup,
GalleryName: tt.fields.GalleryName,
ImageName: tt.fields.ImageName,
ImageVersion: tt.fields.ImageVersion,
TargetRegions: tt.fields.TargetRegions,
ExcludeFromLatest: tt.fields.ExcludeFromLatest,
}
gotErrs, gotWarns := sigd.Validate("sigdest")
var gotStrErrs []string
if gotErrs != nil {
gotStrErrs = make([]string, len(gotErrs))
for i, e := range gotErrs {
gotStrErrs[i] = e.Error()
}
}
if !reflect.DeepEqual(gotStrErrs, tt.wantErrs) {
t.Errorf("SharedImageGalleryDestination.Validate() gotErrs = %q, want %q", gotStrErrs, tt.wantErrs)
}
if !reflect.DeepEqual(gotWarns, tt.wantWarns) {
t.Errorf("SharedImageGalleryDestination.Validate() gotWarns = %q, want %q", gotWarns, tt.wantWarns)
}
})
}
}

View File

@ -8,11 +8,12 @@ import (
"regexp" "regexp"
"testing" "testing"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
"github.com/Azure/go-autorest/autorest"
"github.com/hashicorp/packer/builder/azure/common/client" "github.com/hashicorp/packer/builder/azure/common/client"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
"github.com/Azure/go-autorest/autorest"
) )
func TestStepCreateNewDisk_Run(t *testing.T) { func TestStepCreateNewDisk_Run(t *testing.T) {
@ -33,7 +34,7 @@ func TestStepCreateNewDisk_Run(t *testing.T) {
want multistep.StepAction want multistep.StepAction
}{ }{
{ {
name: "HappyPathDiskSource", name: "from disk",
fields: fields{ fields: fields{
ResourceID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName", ResourceID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName",
DiskSizeGB: 42, DiskSizeGB: 42,
@ -62,7 +63,7 @@ func TestStepCreateNewDisk_Run(t *testing.T) {
want: multistep.ActionContinue, want: multistep.ActionContinue,
}, },
{ {
name: "HappyPathDiskSource", name: "from image",
fields: fields{ fields: fields{
ResourceID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName", ResourceID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName",
DiskStorageAccountType: string(compute.StandardLRS), DiskStorageAccountType: string(compute.StandardLRS),

View File

@ -0,0 +1,110 @@
package chroot
import (
"context"
"io/ioutil"
"net/http"
"reflect"
"regexp"
"testing"
"github.com/hashicorp/packer/builder/azure/common/client"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
"github.com/Azure/go-autorest/autorest"
)
func TestStepCreateSharedImageVersion_Run(t *testing.T) {
type fields struct {
Destination SharedImageGalleryDestination
OSDiskCacheType string
Location string
}
tests := []struct {
name string
fields fields
want multistep.StepAction
expectedPutBody string
}{
{
name: "happy path",
fields: fields{
Destination: SharedImageGalleryDestination{
ResourceGroup: "ResourceGroup",
GalleryName: "GalleryName",
ImageName: "ImageName",
ImageVersion: "0.1.2",
TargetRegions: []TargetRegion{
TargetRegion{
Name: "region1",
ReplicaCount: 5,
StorageAccountType: "Standard_ZRS",
},
},
ExcludeFromLatest: true,
},
Location: "region2",
},
expectedPutBody: `{
"location": "region2",
"properties": {
"publishingProfile": {
"targetRegions": [
{
"name": "region1",
"regionalReplicaCount": 5,
"storageAccountType": "Standard_ZRS"
}
],
"excludeFromLatest": true
},
"storageProfile": {
"osDiskImage": {
"source": {
"id": "osdisksnapshotresourceid"
}
}
}
}
}`,
},
}
for _, tt := range tests {
expectedPutBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.expectedPutBody, "")
m := compute.NewGalleryImageVersionsClient("subscriptionId")
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
if r.Method != "PUT" {
t.Fatal("Expected only a PUT call")
}
b, _ := ioutil.ReadAll(r.Body)
if string(b) != expectedPutBody {
t.Errorf("expected body to be %v, but got %v", expectedPutBody, string(b))
}
return &http.Response{
Request: r,
StatusCode: 200,
}, nil
})
state := new(multistep.BasicStateBag)
state.Put("azureclient", &client.AzureClientSetMock{
GalleryImageVersionsClientMock: m,
})
state.Put("ui", packer.TestUi(t))
state.Put(stateBagKey_OSDiskSnapshotResourceID, "osdisksnapshotresourceid")
t.Run(tt.name, func(t *testing.T) {
s := &StepCreateSharedImageVersion{
Destination: tt.fields.Destination,
OSDiskCacheType: tt.fields.OSDiskCacheType,
Location: tt.fields.Location,
}
if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) {
t.Errorf("StepCreateSharedImageVersion.Run() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -40,10 +40,6 @@ func parseSnapshotResourceID(resourceID string) (subscriptionID, resourceGroup,
return r.SubscriptionID, r.ResourceGroup, r.ResourceName, nil return r.SubscriptionID, r.ResourceGroup, r.ResourceName, nil
} }
const (
stateBagKey_OSDiskSnapshotResourceID = "os_disk_snapshot_resource_id"
)
func (s *StepCreateSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepCreateSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
azcli := state.Get("azureclient").(client.AzureClientSet) azcli := state.Get("azureclient").(client.AzureClientSet)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)

View File

@ -0,0 +1,215 @@
package chroot
import (
"context"
"io/ioutil"
"net/http"
"reflect"
"regexp"
"strings"
"testing"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
"github.com/Azure/go-autorest/autorest"
"github.com/hashicorp/packer/builder/azure/common/client"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
func Test_parseSnapshotResourceID(t *testing.T) {
tests := []struct {
name string
resourceID string
wantSubscriptionID string
wantResourceGroup string
wantSnapshotName string
wantErr bool
}{
{
name: "happy path",
resourceID: "/subscriptions/1234/resourceGroups/rg/providers/microsoft.compute/snapshots/disksnapshot1",
wantErr: false,
wantSubscriptionID: "1234",
wantResourceGroup: "rg",
wantSnapshotName: "disksnapshot1",
},
{
name: "error: nonsense",
resourceID: "nonsense",
wantErr: true,
},
{
name: "error: other resource type",
resourceID: "/subscriptions/1234/resourceGroups/rg/providers/microsoft.compute/disks/disksnapshot1",
wantErr: true,
},
{
name: "error: no name",
resourceID: "/subscriptions/1234/resourceGroups/rg/providers/microsoft.compute/snapshots",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotSubscriptionID, gotResourceGroup, gotSnapshotName, err := parseSnapshotResourceID(tt.resourceID)
if (err != nil) != tt.wantErr {
t.Errorf("parseSnapshotResourceID() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotSubscriptionID != tt.wantSubscriptionID {
t.Errorf("parseSnapshotResourceID() gotSubscriptionID = %v, want %v", gotSubscriptionID, tt.wantSubscriptionID)
}
if gotResourceGroup != tt.wantResourceGroup {
t.Errorf("parseSnapshotResourceID() gotResourceGroup = %v, want %v", gotResourceGroup, tt.wantResourceGroup)
}
if gotSnapshotName != tt.wantSnapshotName {
t.Errorf("parseSnapshotResourceID() gotSnapshotName = %v, want %v", gotSnapshotName, tt.wantSnapshotName)
}
})
}
}
func TestStepCreateSnapshot_Run(t *testing.T) {
type fields struct {
ResourceID string
Location string
}
tests := []struct {
name string
fields fields
want multistep.StepAction
wantSnapshotID string
expectedPutBody string
}{
{
name: "happy path",
fields: fields{
ResourceID: "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/snap1",
Location: "region1",
},
expectedPutBody: `{
"location": "region1",
"properties": {
"creationData": {
"createOption": "Copy",
"sourceResourceId": "osdiskresourceid"
},
"incremental": false
}
}`,
},
{
name: "invalid ResourceID",
fields: fields{
ResourceID: "notaresourceid",
Location: "region1",
},
want: multistep.ActionHalt,
},
}
for _, tt := range tests {
expectedPutBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.expectedPutBody, "")
m := compute.NewSnapshotsClient("subscriptionId")
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
if r.Method != "PUT" {
t.Fatal("Expected only a PUT call")
}
b, _ := ioutil.ReadAll(r.Body)
if string(b) != expectedPutBody {
t.Errorf("expected body to be %v, but got %v", expectedPutBody, string(b))
}
return &http.Response{
Request: r,
StatusCode: 200,
}, nil
})
state := new(multistep.BasicStateBag)
state.Put("azureclient", &client.AzureClientSetMock{
SnapshotsClientMock: m,
})
state.Put("ui", packer.TestUi(t))
state.Put(stateBagKey_OSDiskResourceID, "osdiskresourceid")
t.Run(tt.name, func(t *testing.T) {
s := &StepCreateSnapshot{
ResourceID: tt.fields.ResourceID,
Location: tt.fields.Location,
}
if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) {
t.Errorf("StepCreateSnapshot.Run() = %v, want %v", got, tt.want)
}
if tt.wantSnapshotID != "" {
got := state.Get(stateBagKey_OSDiskSnapshotResourceID).(string)
if !strings.EqualFold(got, tt.wantSnapshotID) {
t.Errorf("OSDiskSnapshotResourceID = %v, want %v", got, tt.wantSnapshotID)
}
}
})
}
}
func TestStepCreateSnapshot_Cleanup_skipped(t *testing.T) {
m := compute.NewSnapshotsClient("subscriptionId")
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
t.Fatalf("clean up should be skipped, did not expect HTTP calls")
return nil, nil
})
state := new(multistep.BasicStateBag)
state.Put("azureclient", &client.AzureClientSetMock{
SnapshotsClientMock: m,
})
state.Put("ui", packer.TestUi(t))
s := &StepCreateSnapshot{
SkipCleanup: true,
}
s.Cleanup(state)
}
func TestStepCreateSnapshot_Cleanup(t *testing.T) {
m := compute.NewSnapshotsClient("subscriptionId")
{
expectedCalls := []string{
"POST /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/snap1/endGetAccess?api-version=2019-07-01",
"DELETE /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/snap1?api-version=2019-07-01",
}
i := 0
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
want := expectedCalls[i]
got := r.Method + " " + r.URL.RequestURI()
if want != got {
t.Errorf("unexpected HTTP call: %v, wanted %v", got, want)
return &http.Response{
Request: r,
StatusCode: 599, // 500 is retried
}, nil
}
i++
return &http.Response{
Request: r,
StatusCode: 200,
}, nil
})
}
state := new(multistep.BasicStateBag)
state.Put("azureclient", &client.AzureClientSetMock{
SnapshotsClientMock: m,
})
state.Put("ui", packer.TestUi(t))
s := &StepCreateSnapshot{
ResourceID: "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/snap1",
Location: "region1",
SkipCleanup: false,
resourceGroup: "rg",
snapshotName: "snap1",
subscriptionID: "1234",
}
s.Cleanup(state)
}

View File

@ -35,7 +35,8 @@ func (s *StepVerifySharedImageDestination) Run(ctx context.Context, state multis
return multistep.ActionHalt return multistep.ActionHalt
} }
imageURI := fmt.Sprintf(".../resourcegroup/%s/providers/Microsoft.Compute/galleries/%s/images/%s", imageURI := fmt.Sprintf("/subscriptions/%s/resourcegroup/%s/providers/Microsoft.Compute/galleries/%s/images/%s",
azcli.SubscriptionID(),
s.Image.ResourceGroup, s.Image.ResourceGroup,
s.Image.GalleryName, s.Image.GalleryName,
s.Image.ImageName, s.Image.ImageName,

View File

@ -0,0 +1,185 @@
package chroot
import (
"context"
"io/ioutil"
"net/http"
"reflect"
"strings"
"testing"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
"github.com/Azure/go-autorest/autorest"
"github.com/hashicorp/packer/builder/azure/common/client"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
func TestStepVerifySharedImageDestination_Run(t *testing.T) {
type fields struct {
Image SharedImageGalleryDestination
Location string
}
tests := []struct {
name string
fields fields
want multistep.StepAction
wantErr string
}{
{
name: "happy path",
want: multistep.ActionContinue,
fields: fields{
Image: SharedImageGalleryDestination{
ResourceGroup: "rg",
GalleryName: "gallery",
ImageName: "image",
ImageVersion: "1.2.3",
},
Location: "region1",
},
},
{
name: "not found",
want: multistep.ActionHalt,
wantErr: `Error retrieving shared image "/subscriptions/subscriptionID/resourcegroup/other-rg/providers/Microsoft.Compute/galleries/gallery/images/image": compute.GalleryImagesClient#Get: Failure responding to request: StatusCode=404 -- Original Error: autorest/azure: Service returned an error. Status=404 Code="NotFound" Message="Not found" `,
fields: fields{
Image: SharedImageGalleryDestination{
ResourceGroup: "other-rg",
GalleryName: "gallery",
ImageName: "image",
ImageVersion: "1.2.3",
},
Location: "region1",
},
},
{
name: "wrong region",
want: multistep.ActionHalt,
wantErr: "Destination shared image resource \"image-resourceid-goes-here\" is in a different location (\"region1\") than this VM (\"other-region\"). Packer does not know how to handle that.",
fields: fields{
Image: SharedImageGalleryDestination{
ResourceGroup: "rg",
GalleryName: "gallery",
ImageName: "image",
ImageVersion: "1.2.3",
},
Location: "other-region",
},
},
{
name: "version exists",
want: multistep.ActionHalt,
wantErr: "Shared image version \"2.3.4\" already exists from image \"image-resourceid-goes-here\".",
fields: fields{
Image: SharedImageGalleryDestination{
ResourceGroup: "rg",
GalleryName: "gallery",
ImageName: "image",
ImageVersion: "2.3.4",
},
Location: "region1",
},
},
{
name: "not Linux",
want: multistep.ActionHalt,
wantErr: "The shared image (\"windows-image-resourceid-goes-here\") is not a Linux image (found \"Windows\"). Currently only Linux images are supported.",
fields: fields{
Image: SharedImageGalleryDestination{
ResourceGroup: "rg",
GalleryName: "gallery",
ImageName: "windowsimage",
ImageVersion: "1.2.3",
},
Location: "region1",
},
},
}
for _, tt := range tests {
gi := compute.NewGalleryImagesClient("subscriptionID")
gi.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
switch {
case r.Method == "GET" && strings.HasPrefix(r.URL.RequestURI(),
"/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/gallery/images/image"):
return &http.Response{
Request: r,
Body: ioutil.NopCloser(strings.NewReader(`{
"id": "image-resourceid-goes-here",
"location": "region1",
"properties": {
"osType": "Linux"
}
}`)),
StatusCode: 200,
}, nil
case r.Method == "GET" && strings.HasPrefix(r.URL.RequestURI(),
"/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/gallery/images/windowsimage"):
return &http.Response{
Request: r,
Body: ioutil.NopCloser(strings.NewReader(`{
"id": "windows-image-resourceid-goes-here",
"location": "region1",
"properties": {
"osType": "Windows"
}
}`)),
StatusCode: 200,
}, nil
}
return &http.Response{
Request: r,
Body: ioutil.NopCloser(strings.NewReader(`{
"Code": "NotFound",
"Message": "Not found"
}`)),
StatusCode: 404,
}, nil
})
giv := compute.NewGalleryImageVersionsClient("subscriptionID")
giv.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
if !(r.Method == "GET" && strings.HasPrefix(r.URL.RequestURI(),
"/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/gallery/images/image/versions")) {
t.Errorf("Unexpected HTTP call: %s %s", r.Method, r.URL.RequestURI())
}
return &http.Response{
Request: r,
Body: ioutil.NopCloser(strings.NewReader(`{
"value": [
{
"name": "2.3.4"
}
]
}`)),
StatusCode: 200,
}, nil
})
state := new(multistep.BasicStateBag)
state.Put("azureclient", &client.AzureClientSetMock{
SubscriptionIDMock: "subscriptionID",
GalleryImagesClientMock: gi,
GalleryImageVersionsClientMock: giv,
})
state.Put("ui", packer.TestUi(t))
t.Run(tt.name, func(t *testing.T) {
s := &StepVerifySharedImageDestination{
Image: tt.fields.Image,
Location: tt.fields.Location,
}
if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) {
t.Errorf("StepVerifySharedImageDestination.Run() = %v, want %v", got, tt.want)
}
})
if err, ok := state.GetOk("error"); ok {
if err.(error).Error() != tt.wantErr {
t.Errorf("Unexpected error, got: %q, want: %q", err, tt.wantErr)
}
} else if tt.wantErr != "" {
t.Errorf("Expected error, but didn't get any")
}
}
}