-
Notifications
You must be signed in to change notification settings - Fork 274
/
cloudstackmachineconfig.go
133 lines (115 loc) Β· 5.15 KB
/
cloudstackmachineconfig.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
package v1alpha1
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"github.com/aws/eks-anywhere/pkg/utils/file"
yamlutil "github.com/aws/eks-anywhere/pkg/utils/yaml"
)
// DefaultCloudStackUser is the default CloudStackMachingConfig username.
const DefaultCloudStackUser = "capc"
// CloudStackMachineConfigKind is the kind value for a CloudStackMachineConfig.
const CloudStackMachineConfigKind = "CloudStackMachineConfig"
// Taken from https://github.com/shapeblue/cloudstack/blob/08bb4ad9fea7e422c3d3ac6d52f4670b1e89eed7/api/src/main/java/com/cloud/vm/VmDetailConstants.java
// These fields should be modeled separately in eks-a and not used by the additionalDetails cloudstack VM field.
var restrictedUserCustomDetails = [...]string{
"keyboard", "cpu.corespersocket", "rootdisksize", "boot.mode", "nameonhypervisor",
"nicAdapter", "rootDiskController", "dataDiskController", "svga.vramSize", "nestedVirtualizationFlag", "ramReservation",
"hypervisortoolsversion", "platform", "timeoffset", "kvm.vnc.port", "kvm.vnc.address", "video.hardware", "video.ram",
"smc.present", "firmware", "cpuNumber", "cpuSpeed", "memory", "cpuOvercommitRatio", "memoryOvercommitRatio",
"Message.ReservedCapacityFreed.Flag", "deployvm", "SSH.PublicKey", "SSH.KeyPairNames", "password", "Encrypted.Password",
"configDriveLocation", "nic", "network", "ip4Address", "ip6Address", "disk", "diskOffering", "configurationId",
"keypairnames", "controlNodeLoginUser",
}
// Used for generating yaml for generate clusterconfig command.
func NewCloudStackMachineConfigGenerate(name string) *CloudStackMachineConfigGenerate {
return &CloudStackMachineConfigGenerate{
TypeMeta: metav1.TypeMeta{
Kind: CloudStackMachineConfigKind,
APIVersion: SchemeBuilder.GroupVersion.String(),
},
ObjectMeta: ObjectMeta{
Name: name,
},
Spec: CloudStackMachineConfigSpec{
ComputeOffering: CloudStackResourceIdentifier{
Id: "",
},
Template: CloudStackResourceIdentifier{
Id: "",
},
Users: []UserConfiguration{{
Name: "capc",
SshAuthorizedKeys: []string{"ssh-rsa AAAA..."},
}},
},
}
}
func (c *CloudStackMachineConfigGenerate) APIVersion() string {
return c.TypeMeta.APIVersion
}
func (c *CloudStackMachineConfigGenerate) Kind() string {
return c.TypeMeta.Kind
}
func (c *CloudStackMachineConfigGenerate) Name() string {
return c.ObjectMeta.Name
}
func GetCloudStackMachineConfigs(fileName string) (map[string]*CloudStackMachineConfig, error) {
configs := make(map[string]*CloudStackMachineConfig)
r, err := file.ReadFile(fileName)
if err != nil {
return nil, err
}
resources, err := yamlutil.SplitDocuments(r)
if err != nil {
return nil, err
}
for _, d := range resources {
var config CloudStackMachineConfig
if err = yaml.UnmarshalStrict(d, &config); err == nil {
if config.Kind == CloudStackMachineConfigKind {
configs[config.Name] = &config
continue
}
}
_ = yaml.Unmarshal(d, &config) // this is to check if there is a bad spec in the file
if config.Kind == CloudStackMachineConfigKind {
return nil, fmt.Errorf("unable to unmarshall content from file due to: %v", err)
}
}
if len(configs) == 0 {
return nil, fmt.Errorf("unable to find kind %v in file", CloudStackMachineConfigKind)
}
return configs, nil
}
func validateCloudStackMachineConfig(machineConfig *CloudStackMachineConfig) error {
if len(machineConfig.Spec.ComputeOffering.Id) == 0 && len(machineConfig.Spec.ComputeOffering.Name) == 0 {
return fmt.Errorf("computeOffering is not set for CloudStackMachineConfig %s. Default computeOffering is not supported in CloudStack, please provide a computeOffering name or ID", machineConfig.Name)
}
if len(machineConfig.Spec.Template.Id) == 0 && len(machineConfig.Spec.Template.Name) == 0 {
return fmt.Errorf("template is not set for CloudStackMachineConfig %s. Default template is not supported in CloudStack, please provide a template name or ID", machineConfig.Name)
}
if err, fieldName, fieldValue := machineConfig.Spec.DiskOffering.Validate(); err != nil {
return fmt.Errorf("machine config %s validation failed: %s: %s invalid, %v", machineConfig.Name, fieldName, fieldValue, err)
}
for _, restrictedKey := range restrictedUserCustomDetails {
if _, found := machineConfig.Spec.UserCustomDetails[restrictedKey]; found {
return fmt.Errorf("restricted key %s found in custom user details", restrictedKey)
}
}
if err := validateAffinityConfig(machineConfig); err != nil {
return err
}
return nil
}
func validateAffinityConfig(machineConfig *CloudStackMachineConfig) error {
if len(machineConfig.Spec.Affinity) > 0 && len(machineConfig.Spec.AffinityGroupIds) > 0 {
return fmt.Errorf("affinity and affinityGroupIds cannot be set at the same time for CloudStackMachineConfig %s. Please provide either one of them or none", machineConfig.Name)
}
if len(machineConfig.Spec.Affinity) > 0 {
if machineConfig.Spec.Affinity != "pro" && machineConfig.Spec.Affinity != "anti" && machineConfig.Spec.Affinity != "no" {
return fmt.Errorf("invalid affinity type %s for CloudStackMachineConfig %s. Please provide \"pro\", \"anti\" or \"no\"", machineConfig.Spec.Affinity, machineConfig.Name)
}
}
return nil
}