This repository has been archived by the owner on Nov 30, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
create.go
178 lines (148 loc) · 6.27 KB
/
create.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
package cloudconfigblob
import (
"context"
"fmt"
"net/url"
"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage"
"github.com/Azure/azure-storage-blob-go/azblob"
corev1alpha1 "github.com/giantswarm/apiextensions/v6/pkg/apis/core/v1alpha1"
"github.com/giantswarm/microerror"
"github.com/giantswarm/operatorkit/v7/pkg/controller/context/reconciliationcanceledcontext"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
capzexp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1beta1"
capiexp "sigs.k8s.io/cluster-api/exp/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/giantswarm/azure-operator/v6/service/controller/blobclient"
"github.com/giantswarm/azure-operator/v6/service/controller/key"
)
// EnsureCreated will make sure that a blob is saved in the Storage Account containing the cloud config for the node pool.
func (r *Resource) EnsureCreated(ctx context.Context, obj interface{}) error {
azureMachinePool, err := key.ToAzureMachinePool(obj)
if err != nil {
return microerror.Mask(err)
}
machinePool, err := r.getOwnerMachinePool(ctx, azureMachinePool.ObjectMeta)
if err != nil {
return microerror.Mask(err)
}
if machinePool == nil {
return microerror.Mask(ownerReferenceNotSet)
}
if !machinePool.GetDeletionTimestamp().IsZero() {
r.logger.Debugf(ctx, "MachinePool is being deleted, skipping saving cloud config in Azure blob")
return nil
}
blobName := key.BootstrapBlobName(azureMachinePool)
var payload string
{
payload, err = r.getCloudConfigFromBootstrapSecret(ctx, machinePool)
if errors.IsNotFound(microerror.Cause(err)) {
r.logger.Debugf(ctx, "bootstrap CR or cloudconfig secret were not found")
r.logger.Debugf(ctx, "cancelling reconciliation")
reconciliationcanceledcontext.SetCanceled(ctx)
return nil
} else if IsBootstrapCRNotReady(err) {
r.logger.Debugf(ctx, "bootstrap CR is not ready")
r.logger.Debugf(ctx, "cancelling reconciliation")
reconciliationcanceledcontext.SetCanceled(ctx)
return nil
} else if err != nil {
return microerror.Mask(err)
}
}
var containerURL azblob.ContainerURL
{
containerURL, err = r.getContainerURL(ctx, &azureMachinePool, key.ClusterID(&azureMachinePool), key.StorageAccountName(&azureMachinePool))
if IsNotFound(err) {
r.logger.Debugf(ctx, "did not find storage account")
r.logger.Debugf(ctx, "canceling resource")
return nil
} else if err != nil {
return microerror.Mask(err)
}
}
{
r.logger.Debugf(ctx, "ensuring container object %#q contains bootstrap config", blobName)
_, err = blobclient.PutBlockBlob(ctx, blobName, payload, &containerURL)
if err != nil {
return microerror.Mask(err)
}
r.logger.Debugf(ctx, "ensured container object %#q contains bootstrap config", blobName)
}
return nil
}
// getCloudConfigFromBootstrapSecret returns the Bootstrap cloud config from the Bootstrap secret.
func (r *Resource) getCloudConfigFromBootstrapSecret(ctx context.Context, machinePool *capiexp.MachinePool) (string, error) {
var err error
var bootstrapSecretName string
{
bootstrapSecretName, err = r.getBootstrapSecretName(ctx, machinePool)
if err != nil {
return "", microerror.Mask(err)
}
}
var cloudconfigSecret corev1.Secret
{
r.logger.Debugf(ctx, "Trying to find Secret containing bootstrap config %#q", bootstrapSecretName)
err := r.ctrlClient.Get(ctx, client.ObjectKey{Namespace: machinePool.Namespace, Name: bootstrapSecretName}, &cloudconfigSecret)
if err != nil {
return "", microerror.Mask(err)
}
}
return string(cloudconfigSecret.Data[key.CloudConfigSecretKey]), nil
}
// getBootstrapSecretName will try to find Ignition CRs instead of Spark CRs when Ignition Operator is deployed.
// It tries to find a Bootstrap CR which is named after the MachinePool. We may want to change it so we use `MachinePool.Spec.Template.Spec.Bootstrap`.
func (r *Resource) getBootstrapSecretName(ctx context.Context, machinePool *capiexp.MachinePool) (string, error) {
var sparkCR corev1alpha1.Spark
{
r.logger.Debugf(ctx, "Trying to find Bootstrap CR %#q", machinePool.Name)
err := r.ctrlClient.Get(ctx, client.ObjectKey{Namespace: machinePool.Namespace, Name: machinePool.Name}, &sparkCR)
if err != nil {
return "", microerror.Mask(err)
}
if !sparkCR.Status.Ready || sparkCR.Status.DataSecretName == "" {
return "", microerror.Mask(bootstrapCRNotReady)
}
}
return sparkCR.Status.DataSecretName, nil
}
func (r *Resource) getContainerURL(ctx context.Context, azureMachinePool *capzexp.AzureMachinePool, resourceGroupName, storageAccountName string) (azblob.ContainerURL, error) {
r.logger.Debugf(ctx, "Finding ContainerURL to upload bootstrap config")
storageAccountsClient, err := r.clientFactory.GetStorageAccountsClient(ctx, azureMachinePool.ObjectMeta)
if err != nil {
return azblob.ContainerURL{}, microerror.Mask(err)
}
primaryKey, err := r.getPrimaryKey(ctx, storageAccountsClient, resourceGroupName, storageAccountName)
if err != nil {
return azblob.ContainerURL{}, microerror.Mask(err)
}
sc, err := azblob.NewSharedKeyCredential(storageAccountName, primaryKey)
if err != nil {
return azblob.ContainerURL{}, microerror.Mask(err)
}
p := azblob.NewPipeline(sc, azblob.PipelineOptions{})
u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net", storageAccountName))
serviceURL := azblob.NewServiceURL(*u, p)
return serviceURL.NewContainerURL(key.BlobContainerName()), nil
}
func (r *Resource) getPrimaryKey(ctx context.Context, storageAccountsClient *storage.AccountsClient, resourceGroupName, storageAccountName string) (string, error) {
r.logger.Debugf(ctx, "Finding PrimaryKey for encryption in Storage Account")
_, err := storageAccountsClient.GetProperties(ctx, resourceGroupName, storageAccountName, storage.AccountExpandGeoReplicationStats)
if err != nil {
return "", microerror.Mask(err)
}
keys, err := storageAccountsClient.ListKeys(ctx, resourceGroupName, storageAccountName, "")
if IsStorageAccountNotProvisioned(err) {
r.logger.Debugf(ctx, "found storage account is not provisioned")
r.logger.Debugf(ctx, "canceling resource")
return "", nil
} else if err != nil {
return "", microerror.Mask(err)
}
if len(*(keys.Keys)) == 0 {
return "", microerror.Maskf(executionFailedError, "storage account key's list is empty")
}
return *(((*keys.Keys)[0]).Value), nil
}