@@ -17,14 +17,19 @@ limitations under the License.
17
17
package v1beta1
18
18
19
19
import (
20
+ "context"
20
21
"fmt"
22
+ "github.com/apache/cloudstack-go/v2/cloudstack"
21
23
"github.com/pkg/errors"
22
24
corev1 "k8s.io/api/core/v1"
23
25
conv "k8s.io/apimachinery/pkg/conversion"
24
26
"sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2"
25
- "strings"
27
+ clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
28
+ "sigs.k8s.io/controller-runtime/pkg/client"
26
29
)
27
30
31
+ const DefaultEndpointCredential = "global"
32
+
28
33
//nolint:golint,revive,stylecheck
29
34
func Convert_v1beta1_CloudStackCluster_To_v1beta2_CloudStackCluster (in * CloudStackCluster , out * v1beta2.CloudStackCluster , s conv.Scope ) error {
30
35
out .ObjectMeta = in .ObjectMeta
@@ -36,27 +41,30 @@ func Convert_v1beta1_CloudStackCluster_To_v1beta2_CloudStackCluster(in *CloudSta
36
41
return err
37
42
}
38
43
}
44
+ failureDomains , err := getFailureDomains (in )
45
+ if err != nil {
46
+ return err
47
+ }
39
48
out .Spec = v1beta2.CloudStackClusterSpec {
40
49
ControlPlaneEndpoint : in .Spec .ControlPlaneEndpoint ,
41
- FailureDomains : getFailureDomains ( in ) ,
50
+ FailureDomains : failureDomains ,
42
51
Account : in .Spec .Account ,
43
52
Domain : in .Spec .Domain ,
44
53
IdentityRef : identifyRef ,
45
54
}
46
55
47
56
zoneStatusMap := v1beta2.ZoneStatusMap {}
48
57
for zoneKey , zone := range in .Status .Zones {
49
- zone1 := zone
50
- zonev2 := & v1beta2.Zone {}
51
- err := Convert_v1beta1_Zone_To_v1beta2_Zone (& zone1 , zonev2 , nil )
58
+ zoneV1 := zone
59
+ zoneV2 := & v1beta2.Zone {}
60
+ err := Convert_v1beta1_Zone_To_v1beta2_Zone (& zoneV1 , zoneV2 , nil )
52
61
if err != nil {
53
62
return err
54
63
}
55
- zoneStatusMap [zoneKey ] = * zonev2
64
+ zoneStatusMap [zoneKey ] = * zoneV2
56
65
}
57
66
out .Status = v1beta2.CloudStackClusterStatus {
58
67
Zones : zoneStatusMap ,
59
- //CloudStackFailureDomainStatusMap: getFailureDomainsStatusMap(in),
60
68
FailureDomains : in .Status .FailureDomains ,
61
69
PublicIPID : in .Status .PublicIPID ,
62
70
PublicIPNetworkID : in .Status .PublicIPNetworkID ,
@@ -125,57 +133,15 @@ func getZones(csCluster *v1beta2.CloudStackCluster) []Zone {
125
133
return zones
126
134
}
127
135
128
- //func getZoneMap(csCluster *v1beta2.CloudStackCluster) (map[string]Zone, error) {
129
- // zoneMap := map[string]Zone{}
130
- // for key := range csCluster.Status.CloudStackFailureDomainStatusMap {
131
- // zone, err := getZoneByMetaName(csCluster, key)
132
- // if err != nil {
133
- // return nil, err
134
- // }
135
- // zoneMap[key] = zone
136
- // }
137
- // return zoneMap, nil
138
- //}
139
- //
140
- //func getDomainID(csCluster *v1beta2.CloudStackCluster) (string, error) {
141
- // var domainID string
142
- // for _, value := range csCluster.Status.CloudStackFailureDomainStatusMap {
143
- // if domainID == "" {
144
- // domainID = value.DomainID
145
- // } else if domainID != value.DomainID {
146
- // return "", errors.Errorf("multiple domainId found in cloudstack failure domain status")
147
- // }
148
- // }
149
- // return domainID, nil
150
- //}
151
- //
152
- //func getZoneByMetaName(csCluster *v1beta2.CloudStackCluster, metaName string) (Zone, error) {
153
- // var zone Zone
154
- // err := errors.Errorf("zone with meta %s not found", metaName)
155
- // for _, failureDomain := range csCluster.Spec.FailureDomains {
156
- // if failureDomain.Zone.MetaName() == metaName {
157
- // err = nil
158
- // zone = Zone{
159
- // ID: failureDomain.Zone.ID,
160
- // Name: failureDomain.Zone.Name,
161
- // Network: Network{
162
- // ID: failureDomain.Zone.Network.ID,
163
- // Name: failureDomain.Zone.Network.Name,
164
- // Type: failureDomain.Zone.Network.Type,
165
- // },
166
- // }
167
- // }
168
- // }
169
- // return zone, err
170
- //}
171
-
172
- // getFailureDomains maps v1beta1 zones to v1beta2 failure domains
173
- func getFailureDomains (csCluster * CloudStackCluster ) []v1beta2.CloudStackFailureDomainSpec {
136
+ // getFailureDomains maps v1beta1 zones to v1beta2 failure domains.
137
+ func getFailureDomains (csCluster * CloudStackCluster ) ([]v1beta2.CloudStackFailureDomainSpec , error ) {
174
138
var failureDomains []v1beta2.CloudStackFailureDomainSpec
175
- index := 0
139
+ namespace := csCluster . Namespace
176
140
for _ , zone := range csCluster .Spec .Zones {
177
- index = index + 1
178
- name := fmt .Sprintf ("%s-%s-%d" , csCluster .Name , "failuredomain" , index )
141
+ name , err := GetDefaultFailureDomainName (namespace , csCluster .Name , zone .ID , zone .Name )
142
+ if err != nil {
143
+ return nil , err
144
+ }
179
145
failureDomains = append (failureDomains , v1beta2.CloudStackFailureDomainSpec {
180
146
Name : name ,
181
147
Zone : v1beta2.CloudStackZoneSpec {
@@ -191,39 +157,81 @@ func getFailureDomains(csCluster *CloudStackCluster) []v1beta2.CloudStackFailure
191
157
Account : csCluster .Spec .Account ,
192
158
ACSEndpoint : corev1.SecretReference {
193
159
Namespace : csCluster .ObjectMeta .Namespace ,
194
- Name : name ,
160
+ Name : DefaultEndpointCredential ,
195
161
},
196
162
})
197
163
}
198
- return failureDomains
164
+ return failureDomains , nil
199
165
}
200
166
201
- //
202
- //func getFailureDomainsStatusMap(csCluster *CloudStackCluster) map[string]v1beta2.CloudStackFailureDomainStatus {
203
- // failureDomainsStatusMap := map[string]v1beta2.CloudStackFailureDomainStatus{}
204
- // for _, zone := range csCluster.Spec.Zones {
205
- // failureDomainsStatusMap[zone.MetaName()] = v1beta2.CloudStackFailureDomainStatus{
206
- // DomainID: csCluster.Status.DomainID,
207
- // Ready: csCluster.Status.Ready,
208
- // }
209
- // }
210
- // return failureDomainsStatusMap
211
- //}
167
+ // GetDefaultFailureDomainName return zoneID as failuredomain name.
168
+ // Default failure domain name is used when migrating an old cluster to a multiple-endpoints supported cluster, that
169
+ // requires to convert each zone to a failure domain.
170
+ // When upgrading cluster using eks-a, a secret named global will be created by eks-a, and it is used by following
171
+ // method to get zoneID by calling cloudstack API.
172
+ // When upgrading cluster using clusterctl directly, zoneID is fetched directly from kubernetes cluster in cloudstackzones.
173
+ func GetDefaultFailureDomainName (namespace string , clusterName string , zoneID string , zoneName string ) (string , error ) {
174
+ if len (zoneID ) > 0 {
175
+ return zoneID , nil
176
+ }
212
177
213
- func GetDefaultFailureDomainName (clusterName string , zoneID string , zoneName string ) (string , error ) {
214
- zoneMetaName := ""
215
- if len (zoneName ) > 0 {
216
- zoneMetaName = zoneName
217
- } else if len (zoneID ) > 0 {
218
- zoneMetaName = zoneID
178
+ // try fetch zoneID using zoneName through cloudstack client
179
+ zoneID , err := fetchZoneIDUsingCloudStack (namespace , zoneName )
180
+ if err == nil {
181
+ return zoneID , nil
219
182
}
220
183
221
- if len (zoneMetaName ) == 0 {
222
- return "" , errors .Errorf ("failed to generate default failureDomainName: zone id and name both empty" )
184
+ return fetchZoneIDUsingK8s (namespace , clusterName , zoneName )
185
+ }
186
+
187
+ func fetchZoneIDUsingK8s (namespace string , clusterName string , zoneName string ) (string , error ) {
188
+ zones := & v1beta2.CloudStackZoneList {}
189
+ capiClusterLabel := map [string ]string {clusterv1 .ClusterLabelName : clusterName }
190
+ if err := v1beta2 .K8sClient .List (
191
+ context .TODO (),
192
+ zones ,
193
+ client .InNamespace (namespace ),
194
+ client .MatchingLabels (capiClusterLabel ),
195
+ ); err != nil {
196
+ return "" , err
223
197
}
224
- if len (clusterName ) == 0 {
225
- return "" , errors .Errorf ("failed to generate default failureDomainName: clusterName empty" )
198
+
199
+ for _ , zone := range zones .Items {
200
+ if zone .Spec .Name == zoneName {
201
+ return zone .Spec .ID , nil
202
+ }
226
203
}
227
204
228
- return strings .ToLower (fmt .Sprintf ("%s-failuredomain-%s" , clusterName , zoneMetaName )), nil
205
+ return "" , errors .Errorf ("failed to generate default failureDomainName: zone id not found for zone name: %s" , zoneName )
206
+ }
207
+
208
+ func fetchZoneIDUsingCloudStack (namespace string , zoneName string ) (string , error ) {
209
+ endpointCredentials := & corev1.Secret {}
210
+ key := client.ObjectKey {Name : DefaultEndpointCredential , Namespace : namespace }
211
+ if err := v1beta2 .K8sClient .Get (context .TODO (), key , endpointCredentials ); err != nil {
212
+ return "" , err
213
+ }
214
+
215
+ config := map [string ]interface {}{}
216
+ for k , v := range endpointCredentials .Data {
217
+ config [k ] = string (v )
218
+ }
219
+ // TODO change secret parsing manner.
220
+ if val , present := config ["verify-ssl" ]; present {
221
+ if val == "true" {
222
+ config ["verify-ssl" ] = true
223
+ } else if val == "false" {
224
+ config ["verify-ssl" ] = false
225
+ }
226
+ }
227
+
228
+ csClient := cloudstack .NewAsyncClient (fmt .Sprint (config ["api-url" ]), fmt .Sprint (config ["api-key" ]), fmt .Sprint (config ["secret-key" ]), fmt .Sprint (config ["verify-ssl" ]) == "true" )
229
+
230
+ if zoneID , count , err := csClient .Zone .GetZoneID (zoneName ); err != nil {
231
+ return "" , err
232
+ } else if count != 1 {
233
+ return "" , errors .Errorf ("%v zones found for zone name %s" , count , zoneName )
234
+ } else {
235
+ return zoneID , nil
236
+ }
229
237
}
0 commit comments