-
Notifications
You must be signed in to change notification settings - Fork 106
/
azure.go
188 lines (158 loc) · 6.15 KB
/
azure.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package azure
import (
"context"
"errors"
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
"github.com/osbuild/osbuild-composer/internal/common"
)
type Client struct {
creds *azidentity.ClientSecretCredential
resFact *armresources.ClientFactory
storFact *armstorage.ClientFactory
}
// NewClient creates a client for accessing the Azure API.
// See https://docs.microsoft.com/en-us/rest/api/azure/
// If you need to work with the Azure Storage API, see NewStorageClient
func NewClient(credentials Credentials, tenantID, subscriptionID string) (*Client, error) {
creds, err := azidentity.NewClientSecretCredential(tenantID, credentials.clientID, credentials.clientSecret, nil)
if err != nil {
return nil, fmt.Errorf("creating azure ClientSecretCredential failed: %v", err)
}
resFact, err := armresources.NewClientFactory(subscriptionID, creds, nil)
if err != nil {
return nil, fmt.Errorf("creating resources client factory failed: %v", err)
}
storFact, err := armstorage.NewClientFactory(subscriptionID, creds, nil)
if err != nil {
return nil, fmt.Errorf("creating storage client factory failed: %v", err)
}
return &Client{
creds,
resFact,
storFact,
}, nil
}
// Tag is a name-value pair representing the tag structure in Azure
type Tag struct {
Name string
Value string
}
// GetResourceNameByTag returns a name of an Azure resource tagged with the
// given `tag`. Note that if multiple resources with the same tag exists
// in the specified resource group, only one name is returned. It's undefined
// which one it is.
func (ac Client) GetResourceNameByTag(ctx context.Context, resourceGroup string, tag Tag) (string, error) {
c := ac.resFact.NewClient()
pager := c.NewListByResourceGroupPager(resourceGroup, &armresources.ClientListByResourceGroupOptions{
Filter: common.ToPtr(fmt.Sprintf("tagName eq '%s' and tagValue eq '%s'", tag.Name, tag.Value)),
})
result, err := pager.NextPage(ctx)
if err != nil {
return "", fmt.Errorf("listing resources failed: %v", err)
}
if len(result.Value) < 1 {
return "", nil
}
return *result.Value[0].Name, nil
}
// GetResourceGroupLocation returns the location of the given resource group.
func (ac Client) GetResourceGroupLocation(ctx context.Context, resourceGroup string) (string, error) {
c := ac.resFact.NewResourceGroupsClient()
group, err := c.Get(ctx, resourceGroup, nil)
if err != nil {
return "", fmt.Errorf("retrieving resource group failed: %v", err)
}
return *group.Location, nil
}
// CreateStorageAccount creates a storage account in the specified resource
// group. The location parameter can be used to specify its location. The tag
// can be used to specify a tag attached to the account.
// The location is optional and if not provided, it is determined
// from the resource group.
func (ac Client) CreateStorageAccount(ctx context.Context, resourceGroup, name, location string, tag Tag) error {
c := ac.storFact.NewAccountsClient()
var err error
if location == "" {
location, err = ac.GetResourceGroupLocation(ctx, resourceGroup)
if err != nil {
return fmt.Errorf("retrieving resource group location failed: %v", err)
}
}
poller, err := c.BeginCreate(ctx, resourceGroup, name, armstorage.AccountCreateParameters{
SKU: &armstorage.SKU{
Name: common.ToPtr(armstorage.SKUNameStandardLRS),
Tier: common.ToPtr(armstorage.SKUTierStandard),
},
Location: &location,
Tags: map[string]*string{
tag.Name: &tag.Value,
},
Properties: &armstorage.AccountPropertiesCreateParameters{
MinimumTLSVersion: common.ToPtr(armstorage.MinimumTLSVersionTLS12),
},
}, nil)
if err != nil {
return fmt.Errorf("sending the create storage account request failed: %v", err)
}
_, err = poller.PollUntilDone(ctx, nil)
if err != nil {
return fmt.Errorf("create storage account request failed: %v", err)
}
return nil
}
// GetStorageAccountKey returns a storage account key that can be used to
// access the given storage account. This method always returns only the first
// key.
func (ac Client) GetStorageAccountKey(ctx context.Context, resourceGroup string, storageAccount string) (string, error) {
c := ac.storFact.NewAccountsClient()
keys, err := c.ListKeys(ctx, resourceGroup, storageAccount, nil)
if err != nil {
return "", fmt.Errorf("retrieving keys for a storage account failed: %v", err)
}
if len(keys.Keys) == 0 {
return "", errors.New("azure returned an empty list of keys")
}
return *keys.Keys[0].Value, nil
}
// RegisterImage creates a generalized V1 Linux image from a given blob.
// The location is optional and if not provided, it is determined
// from the resource group.
func (ac Client) RegisterImage(ctx context.Context, subscriptionID, resourceGroup, storageAccount, storageContainer, blobName, imageName, location string) error {
c, err := armcompute.NewImagesClient(subscriptionID, ac.creds, nil)
if err != nil {
return fmt.Errorf("unable to create compute client: %v", err)
}
blobURI := fmt.Sprintf("https://%s.blob.core.windows.net/%s/%s", storageAccount, storageContainer, blobName)
if location == "" {
location, err = ac.GetResourceGroupLocation(ctx, resourceGroup)
if err != nil {
return fmt.Errorf("retrieving resource group location failed: %v", err)
}
}
imageFuture, err := c.BeginCreateOrUpdate(ctx, resourceGroup, imageName, armcompute.Image{
Properties: &armcompute.ImageProperties{
HyperVGeneration: common.ToPtr(armcompute.HyperVGenerationTypesV1),
SourceVirtualMachine: nil,
StorageProfile: &armcompute.ImageStorageProfile{
OSDisk: &armcompute.ImageOSDisk{
OSType: common.ToPtr(armcompute.OperatingSystemTypesLinux),
BlobURI: &blobURI,
OSState: common.ToPtr(armcompute.OperatingSystemStateTypesGeneralized),
},
},
},
Location: &location,
}, nil)
if err != nil {
return fmt.Errorf("sending the create image request failed: %v", err)
}
_, err = imageFuture.PollUntilDone(ctx, nil)
if err != nil {
return fmt.Errorf("create image request failed: %v", err)
}
return nil
}