Skip to content

Commit b166131

Browse files
krsriSridharan
andauthored
Support multiple tags (#701)
Co-authored-by: Sridharan <[email protected]>
1 parent 294d524 commit b166131

File tree

2 files changed

+111
-2
lines changed

2 files changed

+111
-2
lines changed

vmlifecycle/vmmanagers/azure.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,48 @@ type AzureVMManager struct {
9191
pollingInterval time.Duration
9292
}
9393

94+
func ParseTagsString(tagsStr string) []string {
95+
var tags []string
96+
var current strings.Builder
97+
inQuotes := false
98+
quoteChar := byte(0)
99+
100+
for i := 0; i < len(tagsStr); i++ {
101+
char := tagsStr[i]
102+
103+
if inQuotes {
104+
if char == quoteChar {
105+
// End of quoted string
106+
inQuotes = false
107+
quoteChar = 0
108+
} else {
109+
current.WriteByte(char)
110+
}
111+
} else {
112+
if char == '"' || char == '\'' {
113+
// Start of quoted string
114+
inQuotes = true
115+
quoteChar = char
116+
} else if char == ' ' {
117+
// Space → end of tag
118+
if current.Len() > 0 {
119+
tags = append(tags, strings.TrimSpace(current.String()))
120+
current.Reset()
121+
}
122+
} else {
123+
current.WriteByte(char)
124+
}
125+
}
126+
}
127+
128+
// Add last tag if buffer not empty
129+
if current.Len() > 0 {
130+
tags = append(tags, strings.TrimSpace(current.String()))
131+
}
132+
133+
return tags
134+
}
135+
94136
func NewAzureVMManager(stdout, stderr io.Writer, config *OpsmanConfigFilePayload, imageYaml string, state StateInfo, azureRunner azureRunner, t time.Duration) *AzureVMManager {
95137
return &AzureVMManager{
96138
stdout: stdout,
@@ -506,7 +548,12 @@ func (a *AzureVMManager) createVM(imageUrl string, imageID string, publicIP stri
506548
}
507549

508550
if azure.Tags != "" {
509-
args = append(args, "--tags", azure.Tags)
551+
// Parse tags string respecting quoted values that may contain spaces
552+
tags := ParseTagsString(azure.Tags)
553+
args = append(args, "--tags")
554+
for _, tag := range tags {
555+
args = append(args, tag)
556+
}
510557
}
511558

512559
_, _, err = a.runner.ExecuteWithEnvVars(a.addEnvVars(), args)

vmlifecycle/vmmanagers/azure_test.go

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ opsman-configuration:
211211
"--private-ip-address", "10.0.0.3",
212212
"--storage-sku", "Standard_LRS",
213213
"--image", "/some/resource/id/image",
214-
"--tags", "Project=ECommerce CostCenter=00123 Team=Web",
214+
"--tags", "Project=ECommerce", "CostCenter=00123", "Team=Web",
215215
},
216216
{
217217
"storage", "blob", "delete-batch",
@@ -227,6 +227,68 @@ opsman-configuration:
227227
}
228228
})
229229

230+
It("passes multiple tags as separate arguments", func() {
231+
const configWithMultipleTags = `
232+
opsman-configuration:
233+
azure:
234+
vm_name: vm-name
235+
subscription_id: subscription
236+
resource_group: rg
237+
tenant_id: tenant
238+
client_id: client
239+
client_secret: secret
240+
location: %s
241+
public_ip: 1.2.3.4
242+
private_ip: 10.0.0.3
243+
container: opsman_container
244+
boot_disk_size: 200
245+
network_security_group: nsg
246+
subnet_id: /subscriptions/sub-guid/resourceGroups/network-resource-group/providers/Microsoft.Network/virtualNetworks/vnet1/subnets/sub1
247+
storage_account: account
248+
storage_key: key
249+
ssh_public_key: asdfasdfs
250+
cloud_name: AzureGermanCloud
251+
tags: "foo 'bar=single quote' \"key=double quote\" key1=\"value\" key2=value2 key3=\"value with spaces\" key4='value with single quotes' key5=simple key6=\"quoted=equals\" key7='single=equals'"
252+
`
253+
setupCommand(configWithMultipleTags)
254+
status, stateInfo, err := command.CreateVM()
255+
Expect(err).ToNot(HaveOccurred())
256+
Expect(status).To(Equal(vmmanagers.Success))
257+
Expect(stateInfo).To(Equal(vmmanagers.StateInfo{IAAS: "azure", ID: "vm-name"}))
258+
259+
// Find the vm create command call
260+
var vmCreateArgs []interface{}
261+
for i := 0; i < runner.ExecuteWithEnvVarsCallCount(); i++ {
262+
_, args := runner.ExecuteWithEnvVarsArgsForCall(i)
263+
if len(args) > 0 && args[0] == "vm" && args[1] == "create" {
264+
vmCreateArgs = args
265+
break
266+
}
267+
}
268+
Expect(vmCreateArgs).ToNot(BeNil())
269+
270+
// Verify that tags are passed as separate arguments
271+
Expect(vmCreateArgs).To(ContainElement("--tags"))
272+
273+
// Test key without value
274+
Expect(vmCreateArgs).To(ContainElement("foo"))
275+
276+
// Test simple key=value pairs
277+
Expect(vmCreateArgs).To(ContainElement("key2=value2"))
278+
Expect(vmCreateArgs).To(ContainElement("key5=simple"))
279+
280+
// Test single quotes
281+
Expect(vmCreateArgs).To(ContainElement("bar=single quote"))
282+
Expect(vmCreateArgs).To(ContainElement("key4=value with single quotes"))
283+
Expect(vmCreateArgs).To(ContainElement("key7=single=equals"))
284+
285+
// Test double quotes
286+
Expect(vmCreateArgs).To(ContainElement("key=double quote"))
287+
Expect(vmCreateArgs).To(ContainElement("key1=value"))
288+
Expect(vmCreateArgs).To(ContainElement("key3=value with spaces"))
289+
Expect(vmCreateArgs).To(ContainElement("key6=quoted=equals"))
290+
})
291+
230292
Context("DEPRECATED tests", func() {
231293
const configWithManagedDiskAndUnmanagedDiskStrTemplateDEPRECATED = `
232294
opsman-configuration:

0 commit comments

Comments
 (0)