diff --git a/docs/docs.go b/docs/docs.go index df7731d4..d5e4a142 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1746,6 +1746,9 @@ const docTemplate = `{ "aws_auth": { "$ref": "#/definitions/types.AWSAuth" }, + "azure_auth": { + "$ref": "#/definitions/types.AzureAuth" + }, "civo_auth": { "$ref": "#/definitions/types.CivoAuth" }, diff --git a/docs/swagger.json b/docs/swagger.json index e7ac7d43..ddfd6013 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1740,6 +1740,9 @@ "aws_auth": { "$ref": "#/definitions/types.AWSAuth" }, + "azure_auth": { + "$ref": "#/definitions/types.AzureAuth" + }, "civo_auth": { "$ref": "#/definitions/types.CivoAuth" }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f61e9a55..607065dd 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -509,6 +509,8 @@ definitions: $ref: '#/definitions/types.AkamaiAuth' aws_auth: $ref: '#/definitions/types.AWSAuth' + azure_auth: + $ref: '#/definitions/types.AzureAuth' civo_auth: $ref: '#/definitions/types.CivoAuth' cloud_region: diff --git a/go.mod b/go.mod index 1ed729c5..046e303b 100644 --- a/go.mod +++ b/go.mod @@ -62,6 +62,8 @@ require ( require ( dario.cat/mergo v1.0.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/docker/go-connections v0.4.0 // indirect diff --git a/go.sum b/go.sum index 75f8d73d..d57eed84 100644 --- a/go.sum +++ b/go.sum @@ -67,10 +67,14 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 h1:/Di3vB4sNeQ+7A8efjUVENvyB945Wruvstucqp7ZArg= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0/go.mod h1:gM3K25LQlsET3QR+4V74zxCsFAy0r6xMNN9n80SZn+4= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 h1:wxQx2Bt4xzPIKvW59WQf1tJNx/ZZKPfN+EhPX3Z6CYY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0/go.mod h1:TpiwjwnW/khS0LKs4vW5UmmT9OWcxaveS8U7+tlknzo= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 h1:cf+OIKbkmMHBaC3u78AXomweqM0oxQSgBXRZf3WH4yM= diff --git a/internal/azure/azure.go b/internal/azure/azure.go index 29ee8a57..91ee5da7 100644 --- a/internal/azure/azure.go +++ b/internal/azure/azure.go @@ -7,8 +7,10 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" ) @@ -43,6 +45,14 @@ func (c *Client) newResourceClientFactory() (*armresources.ClientFactory, error) return client, nil } +func (c *Client) newSubscriptionClientFactory() (*armsubscriptions.ClientFactory, error) { + client, err := armsubscriptions.NewClientFactory(c.cred, nil) + if err != nil { + return nil, fmt.Errorf("failed to create armsubscriptions client: %w", err) + } + return client, nil +} + func (c *Client) newStorageClientFactory() (*armstorage.ClientFactory, error) { client, err := armstorage.NewClientFactory(c.subscriptionID, c.cred, nil) if err != nil { @@ -51,6 +61,14 @@ func (c *Client) newStorageClientFactory() (*armstorage.ClientFactory, error) { return client, nil } +func (c *Client) newVirtualMachineSizesClient() (*armcompute.VirtualMachineSizesClient, error) { + client, err := armcompute.NewVirtualMachineSizesClient(c.subscriptionID, c.cred, nil) + if err != nil { + return nil, fmt.Errorf("failed to create virtualmachine client: %w", err) + } + return client, nil +} + func (c *Client) CreateBlobContainer(ctx context.Context, storageAccountName, containerName string) (*azblob.CreateContainerResponse, error) { client, err := azblob.NewClient(fmt.Sprintf("https://%s.blob.core.windows.net", storageAccountName), c.cred, nil) if err != nil { @@ -135,6 +153,56 @@ func (c *Client) CreateStorageAccount(ctx context.Context, location, resourceGro return &resp.Account, nil } +func (c *Client) GetInstanceSizes(ctx context.Context, location string) ([]string, error) { + client, err := c.newVirtualMachineSizesClient() + if err != nil { + return nil, err + } + + var sizes []string + + pager := client.NewListPager(location, nil) + + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list instance sizes: %w", err) + } + + for _, v := range page.Value { + sizes = append(sizes, *v.Name) + } + } + + return sizes, nil +} + +func (c *Client) GetRegions(ctx context.Context) ([]string, error) { + client, err := c.newSubscriptionClientFactory() + if err != nil { + return nil, err + } + + pager := client.NewClient().NewListLocationsPager(c.subscriptionID, &armsubscriptions.ClientListLocationsOptions{ + IncludeExtendedLocations: to.Ptr(false), + }) + + var regions []string + + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list regions: %w", err) + } + + for _, v := range page.Value { + regions = append(regions, *v.Name) + } + } + + return regions, nil +} + func (c *Client) GetStorageAccessKeys(ctx context.Context, resourceGroup, storageAccountName string) (*Keys, error) { client, err := c.newStorageClientFactory() if err != nil { diff --git a/internal/router/api/v1/instanceSizes.go b/internal/router/api/v1/instanceSizes.go index 9c64e4c6..3b1c3db2 100644 --- a/internal/router/api/v1/instanceSizes.go +++ b/internal/router/api/v1/instanceSizes.go @@ -8,6 +8,7 @@ import ( "github.com/gin-gonic/gin" awsinternal "github.com/konstructio/kubefirst-api/internal/aws" + "github.com/konstructio/kubefirst-api/internal/azure" "github.com/konstructio/kubefirst-api/internal/civo" "github.com/konstructio/kubefirst-api/internal/digitalocean" "github.com/konstructio/kubefirst-api/internal/types" @@ -108,7 +109,38 @@ func ListInstanceSizesForRegion(c *gin.Context) { instanceSizesResponse.InstanceSizes = instanceSizes c.JSON(http.StatusOK, instanceSizesResponse) return + case "azure": + if instanceSizesRequest.AzureAuth.ClientID == "" || + instanceSizesRequest.AzureAuth.ClientSecret == "" || + instanceSizesRequest.AzureAuth.SubscriptionID == "" || + instanceSizesRequest.AzureAuth.TenantID == "" { + c.JSON(http.StatusBadRequest, types.JSONFailureResponse{ + Message: "missing authentication credentials in request, please check and try again", + }) + return + } + azureClient, err := azure.NewClient( + instanceSizesRequest.AzureAuth.ClientID, + instanceSizesRequest.AzureAuth.ClientSecret, + instanceSizesRequest.AzureAuth.SubscriptionID, + instanceSizesRequest.AzureAuth.TenantID, + ) + if err != nil { + c.JSON(http.StatusBadRequest, types.JSONFailureResponse{ + Message: err.Error(), + }) + return + } + + instances, err := azureClient.GetInstanceSizes(context.Background(), instanceSizesRequest.CloudRegion) + if err != nil { + c.JSON(http.StatusBadRequest, types.JSONFailureResponse{ + Message: err.Error(), + }) + return + } + instanceSizesResponse.InstanceSizes = instances case "digitalocean": if instanceSizesRequest.DigitaloceanAuth.Token == "" { c.JSON(http.StatusBadRequest, types.JSONFailureResponse{ diff --git a/internal/router/api/v1/region.go b/internal/router/api/v1/region.go index 6cdf6c26..b790b1e1 100644 --- a/internal/router/api/v1/region.go +++ b/internal/router/api/v1/region.go @@ -14,6 +14,7 @@ import ( "github.com/gin-gonic/gin" awsinternal "github.com/konstructio/kubefirst-api/internal/aws" + "github.com/konstructio/kubefirst-api/internal/azure" "github.com/konstructio/kubefirst-api/internal/civo" "github.com/konstructio/kubefirst-api/internal/digitalocean" "github.com/konstructio/kubefirst-api/internal/types" @@ -98,6 +99,39 @@ func PostRegions(c *gin.Context) { }) return } + regionListResponse.Regions = regions + case "azure": + if regionListRequest.AzureAuth.ClientID == "" || + regionListRequest.AzureAuth.ClientSecret == "" || + regionListRequest.AzureAuth.SubscriptionID == "" || + regionListRequest.AzureAuth.TenantID == "" { + c.JSON(http.StatusBadRequest, types.JSONFailureResponse{ + Message: "missing authentication credentials in request, please check and try again", + }) + return + } + + azureClient, err := azure.NewClient( + regionListRequest.AzureAuth.ClientID, + regionListRequest.AzureAuth.ClientSecret, + regionListRequest.AzureAuth.SubscriptionID, + regionListRequest.AzureAuth.TenantID, + ) + if err != nil { + c.JSON(http.StatusBadRequest, types.JSONFailureResponse{ + Message: err.Error(), + }) + return + } + + regions, err := azureClient.GetRegions(context.Background()) + if err != nil { + c.JSON(http.StatusBadRequest, types.JSONFailureResponse{ + Message: err.Error(), + }) + return + } + regionListResponse.Regions = regions case "civo": if regionListRequest.CivoAuth.Token == "" { diff --git a/internal/types/instanceSizes.go b/internal/types/instanceSizes.go index 3a975861..3fdea80b 100644 --- a/internal/types/instanceSizes.go +++ b/internal/types/instanceSizes.go @@ -16,6 +16,7 @@ type InstanceSizesRequest struct { CivoAuth pkgtypes.CivoAuth `json:"civo_auth,omitempty"` AkamaiAuth pkgtypes.AkamaiAuth `json:"akamai_auth,omitempty"` AWSAuth pkgtypes.AWSAuth `json:"aws_auth,omitempty"` + AzureAuth pkgtypes.AzureAuth `json:"azure_auth,omitempty"` DigitaloceanAuth pkgtypes.DigitaloceanAuth `json:"do_auth,omitempty"` VultrAuth pkgtypes.VultrAuth `json:"vultr_auth,omitempty"` GoogleAuth pkgtypes.GoogleAuth `json:"google_auth,omitempty"` diff --git a/internal/types/region.go b/internal/types/region.go index b31008c4..9a5b5e0f 100644 --- a/internal/types/region.go +++ b/internal/types/region.go @@ -15,6 +15,7 @@ type RegionListRequest struct { CloudRegion string `json:"cloud_region,omitempty"` AkamaiAuth pkgtypes.AkamaiAuth `json:"akamai_auth,omitempty"` AWSAuth pkgtypes.AWSAuth `json:"aws_auth,omitempty"` + AzureAuth pkgtypes.AzureAuth `json:"azure_auth,omitempty"` CivoAuth pkgtypes.CivoAuth `json:"civo_auth,omitempty"` DigitaloceanAuth pkgtypes.DigitaloceanAuth `json:"do_auth,omitempty"` VultrAuth pkgtypes.VultrAuth `json:"vultr_auth,omitempty"`