Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion api/v1beta1/azuremachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ type AzureMachineStatus struct {
}

// +kubebuilder:object:root=true
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.ready",description="AzureMachine ready status"
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status"
// +kubebuilder:printcolumn:name="Reason",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].reason"
// +kubebuilder:printcolumn:name="Message",type="string",priority=1,JSONPath=".status.conditions[?(@.type=='Ready')].message"
// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.vmState",description="Azure VM provisioning state"
// +kubebuilder:printcolumn:name="Cluster",type="string",priority=1,JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this AzureMachine belongs"
// +kubebuilder:printcolumn:name="Machine",type="string",priority=1,JSONPath=".metadata.ownerReferences[?(@.kind==\"Machine\")].name",description="Machine object to which this AzureMachine belongs"
Expand Down
34 changes: 17 additions & 17 deletions azure/converters/futures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"testing"

azureautorest "github.com/Azure/go-autorest/autorest/azure"
"github.com/onsi/gomega"
. "github.com/onsi/gomega"

infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
)
Expand Down Expand Up @@ -76,7 +76,7 @@ func Test_SDKToFuture(t *testing.T) {
service string
resourceName string
rgName string
expect func(*gomega.GomegaWithT, *infrav1.Future, error)
expect func(*GomegaWithT, *infrav1.Future, error)
}{
{
name: "valid future",
Expand All @@ -85,9 +85,9 @@ func Test_SDKToFuture(t *testing.T) {
service: "test-service",
resourceName: "test-resource",
rgName: "test-group",
expect: func(g *gomega.GomegaWithT, f *infrav1.Future, err error) {
g.Expect(err).Should(gomega.BeNil())
g.Expect(f).Should(gomega.BeEquivalentTo(&infrav1.Future{
expect: func(g *GomegaWithT, f *infrav1.Future, err error) {
g.Expect(err).Should(BeNil())
g.Expect(f).Should(BeEquivalentTo(&infrav1.Future{
Type: infrav1.DeleteFuture,
ServiceName: "test-service",
Name: "test-resource",
Expand All @@ -102,7 +102,7 @@ func Test_SDKToFuture(t *testing.T) {
c := c
t.Run(c.name, func(t *testing.T) {
t.Parallel()
g := gomega.NewGomegaWithT(t)
g := NewGomegaWithT(t)
result, err := SDKToFuture(c.future, c.futureType, c.service, c.resourceName, c.rgName)
c.expect(g, result, err)
})
Expand All @@ -113,35 +113,35 @@ func Test_FutureToSDK(t *testing.T) {
cases := []struct {
name string
future infrav1.Future
expect func(*gomega.GomegaWithT, azureautorest.FutureAPI, error)
expect func(*GomegaWithT, azureautorest.FutureAPI, error)
}{
{
name: "data is empty",
future: emptyDataFuture,
expect: func(g *gomega.GomegaWithT, f azureautorest.FutureAPI, err error) {
g.Expect(err.Error()).Should(gomega.ContainSubstring("failed to unmarshal future data"))
expect: func(g *GomegaWithT, f azureautorest.FutureAPI, err error) {
g.Expect(err.Error()).Should(ContainSubstring("failed to unmarshal future data"))
},
},
{
name: "data is not base64 encoded",
future: decodedDataFuture,
expect: func(g *gomega.GomegaWithT, f azureautorest.FutureAPI, err error) {
g.Expect(err.Error()).Should(gomega.ContainSubstring("failed to base64 decode future data"))
expect: func(g *GomegaWithT, f azureautorest.FutureAPI, err error) {
g.Expect(err.Error()).Should(ContainSubstring("failed to base64 decode future data"))
},
},
{
name: "base64 data is not a valid future",
future: invalidFuture,
expect: func(g *gomega.GomegaWithT, f azureautorest.FutureAPI, err error) {
g.Expect(err.Error()).Should(gomega.ContainSubstring("failed to unmarshal future data"))
expect: func(g *GomegaWithT, f azureautorest.FutureAPI, err error) {
g.Expect(err.Error()).Should(ContainSubstring("failed to unmarshal future data"))
},
},
{
name: "valid future data",
future: validFuture,
expect: func(g *gomega.GomegaWithT, f azureautorest.FutureAPI, err error) {
g.Expect(err).Should(gomega.BeNil())
g.Expect(f).Should(gomega.BeAssignableToTypeOf(&azureautorest.Future{}))
expect: func(g *GomegaWithT, f azureautorest.FutureAPI, err error) {
g.Expect(err).Should(BeNil())
g.Expect(f).Should(BeAssignableToTypeOf(&azureautorest.Future{}))
},
},
}
Expand All @@ -150,7 +150,7 @@ func Test_FutureToSDK(t *testing.T) {
c := c
t.Run(c.name, func(t *testing.T) {
t.Parallel()
g := gomega.NewGomegaWithT(t)
g := NewGomegaWithT(t)
result, err := FutureToSDK(c.future)
c.expect(g, result, err)
})
Expand Down
23 changes: 23 additions & 0 deletions azure/converters/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,29 @@ import (
// ErrUserAssignedIdentitiesNotFound is the error thrown when user assigned identities is not passed with the identity type being UserAssigned.
var ErrUserAssignedIdentitiesNotFound = errors.New("the user-assigned identity provider ids must not be null or empty for 'UserAssigned' identity type")

// VMIdentityToVMSDK converts CAPZ VM identity to Azure SDK identity.
func VMIdentityToVMSDK(identity infrav1.VMIdentity, uami []infrav1.UserAssignedIdentity) (*compute.VirtualMachineIdentity, error) {
if identity == infrav1.VMIdentitySystemAssigned {
return &compute.VirtualMachineIdentity{
Type: compute.ResourceIdentityTypeSystemAssigned,
}, nil
}

if identity == infrav1.VMIdentityUserAssigned {
userIdentitiesMap, err := UserAssignedIdentitiesToVMSDK(uami)
if err != nil {
return nil, errors.Wrap(err, "failed to assign VM identity")
}

return &compute.VirtualMachineIdentity{
Type: compute.ResourceIdentityTypeUserAssigned,
UserAssignedIdentities: userIdentitiesMap,
}, nil
}

return nil, nil
}

// UserAssignedIdentitiesToVMSDK converts CAPZ user assigned identities associated with the Virtual Machine to Azure SDK identities
// The user identity dictionary key references will be ARM resource ids in the form:
// '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}'.
Expand Down
91 changes: 76 additions & 15 deletions azure/converters/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"testing"

"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-04-01/compute"
"github.com/onsi/gomega"
. "github.com/onsi/gomega"

infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
)
Expand Down Expand Up @@ -49,26 +49,87 @@ var expectedVMSSSDKObject = map[string]*compute.VirtualMachineScaleSetIdentityUs
"/without/prefix": {},
}

func Test_VMIdentityToVMSDK(t *testing.T) {
cases := []struct {
Name string
identityType infrav1.VMIdentity
uami []infrav1.UserAssignedIdentity
Expect func(*GomegaWithT, *compute.VirtualMachineIdentity, error)
}{
{
Name: "Should return a system assigned identity when identity is system assigned",
identityType: infrav1.VMIdentitySystemAssigned,
Expect: func(g *GomegaWithT, m *compute.VirtualMachineIdentity, err error) {
g.Expect(err).Should(BeNil())
g.Expect(m).Should(Equal(&compute.VirtualMachineIdentity{
Type: compute.ResourceIdentityTypeSystemAssigned,
}))
},
},
{
Name: "Should return user assigned identities when identity is user assigned",
identityType: infrav1.VMIdentityUserAssigned,
uami: []infrav1.UserAssignedIdentity{{ProviderID: "my-uami-1"}, {ProviderID: "my-uami-2"}},
Expect: func(g *GomegaWithT, m *compute.VirtualMachineIdentity, err error) {
g.Expect(err).Should(BeNil())
g.Expect(m).Should(Equal(&compute.VirtualMachineIdentity{
Type: compute.ResourceIdentityTypeUserAssigned,
UserAssignedIdentities: map[string]*compute.VirtualMachineIdentityUserAssignedIdentitiesValue{
"my-uami-1": {},
"my-uami-2": {},
},
}))
},
},
{
Name: "Should fail when no user assigned identities are specified and identity is user assigned",
identityType: infrav1.VMIdentityUserAssigned,
uami: []infrav1.UserAssignedIdentity{},
Expect: func(g *GomegaWithT, m *compute.VirtualMachineIdentity, err error) {
g.Expect(err.Error()).Should(ContainSubstring(ErrUserAssignedIdentitiesNotFound.Error()))
},
},
{
Name: "Should return nil if no known identity is specified",
identityType: "",
Expect: func(g *GomegaWithT, m *compute.VirtualMachineIdentity, err error) {
g.Expect(err).Should(BeNil())
g.Expect(m).Should(BeNil())
},
},
}

for _, c := range cases {
c := c
t.Run(c.Name, func(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)
subject, err := VMIdentityToVMSDK(c.identityType, c.uami)
c.Expect(g, subject, err)
})
}
}

func Test_UserAssignedIdentitiesToVMSDK(t *testing.T) {
cases := []struct {
Name string
SubjectFactory []infrav1.UserAssignedIdentity
Expect func(*gomega.GomegaWithT, map[string]*compute.VirtualMachineIdentityUserAssignedIdentitiesValue, error)
Expect func(*GomegaWithT, map[string]*compute.VirtualMachineIdentityUserAssignedIdentitiesValue, error)
}{
{
Name: "ShouldPopulateWithData",
SubjectFactory: sampleSubjectFactory,
Expect: func(g *gomega.GomegaWithT, m map[string]*compute.VirtualMachineIdentityUserAssignedIdentitiesValue, err error) {
g.Expect(err).Should(gomega.BeNil())
g.Expect(m).Should(gomega.Equal(expectedVMSDKObject))
Expect: func(g *GomegaWithT, m map[string]*compute.VirtualMachineIdentityUserAssignedIdentitiesValue, err error) {
g.Expect(err).Should(BeNil())
g.Expect(m).Should(Equal(expectedVMSDKObject))
},
},

{
Name: "ShouldFailWithError",
SubjectFactory: []infrav1.UserAssignedIdentity{},
Expect: func(g *gomega.GomegaWithT, m map[string]*compute.VirtualMachineIdentityUserAssignedIdentitiesValue, err error) {
g.Expect(err).Should(gomega.Equal(ErrUserAssignedIdentitiesNotFound))
Expect: func(g *GomegaWithT, m map[string]*compute.VirtualMachineIdentityUserAssignedIdentitiesValue, err error) {
g.Expect(err).Should(Equal(ErrUserAssignedIdentitiesNotFound))
},
},
}
Expand All @@ -77,7 +138,7 @@ func Test_UserAssignedIdentitiesToVMSDK(t *testing.T) {
c := c
t.Run(c.Name, func(t *testing.T) {
t.Parallel()
g := gomega.NewGomegaWithT(t)
g := NewGomegaWithT(t)
subject, err := UserAssignedIdentitiesToVMSDK(c.SubjectFactory)
c.Expect(g, subject, err)
})
Expand All @@ -88,22 +149,22 @@ func Test_UserAssignedIdentitiesToVMSSSDK(t *testing.T) {
cases := []struct {
Name string
SubjectFactory []infrav1.UserAssignedIdentity
Expect func(*gomega.GomegaWithT, map[string]*compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue, error)
Expect func(*GomegaWithT, map[string]*compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue, error)
}{
{
Name: "ShouldPopulateWithData",
SubjectFactory: sampleSubjectFactory,
Expect: func(g *gomega.GomegaWithT, m map[string]*compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue, err error) {
g.Expect(err).Should(gomega.BeNil())
g.Expect(m).Should(gomega.Equal(expectedVMSSSDKObject))
Expect: func(g *GomegaWithT, m map[string]*compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue, err error) {
g.Expect(err).Should(BeNil())
g.Expect(m).Should(Equal(expectedVMSSSDKObject))
},
},

{
Name: "ShouldFailWithError",
SubjectFactory: []infrav1.UserAssignedIdentity{},
Expect: func(g *gomega.GomegaWithT, m map[string]*compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue, err error) {
g.Expect(err).Should(gomega.Equal(ErrUserAssignedIdentitiesNotFound))
Expect: func(g *GomegaWithT, m map[string]*compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue, err error) {
g.Expect(err).Should(Equal(ErrUserAssignedIdentitiesNotFound))
},
},
}
Expand All @@ -112,7 +173,7 @@ func Test_UserAssignedIdentitiesToVMSSSDK(t *testing.T) {
c := c
t.Run(c.Name, func(t *testing.T) {
t.Parallel()
g := gomega.NewGomegaWithT(t)
g := NewGomegaWithT(t)
subject, err := UserAssignedIdentitiesToVMSSSDK(c.SubjectFactory)
c.Expect(g, subject, err)
})
Expand Down
25 changes: 25 additions & 0 deletions azure/converters/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"

"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-04-01/compute"
"github.com/Azure/go-autorest/autorest/to"
"github.com/pkg/errors"

infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
Expand Down Expand Up @@ -67,3 +68,27 @@ func specificImageToSDK(image *infrav1.Image) (*compute.ImageReference, error) {
ID: image.ID,
}, nil
}

// ImageToPlan converts a CAPZ Image to an Azure Compute Plan.
func ImageToPlan(image *infrav1.Image) *compute.Plan {
// Plan is needed when using a Shared Gallery image with Plan details.
if image.SharedGallery != nil && image.SharedGallery.Publisher != nil && image.SharedGallery.SKU != nil && image.SharedGallery.Offer != nil {
Comment thread
CecileRobertMichon marked this conversation as resolved.
return &compute.Plan{
Publisher: image.SharedGallery.Publisher,
Name: image.SharedGallery.SKU,
Product: image.SharedGallery.Offer,
}
}

// Plan is needed for third party Marketplace images.
if image.Marketplace != nil && image.Marketplace.ThirdPartyImage {
return &compute.Plan{
Publisher: to.StringPtr(image.Marketplace.Publisher),
Name: to.StringPtr(image.Marketplace.SKU),
Product: to.StringPtr(image.Marketplace.Offer),
}
}

// Otherwise return nil.
return nil
}
Loading