| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package cloudstack |
| |
| import ( |
| "strings" |
| "testing" |
| |
| "github.com/apache/cloudstack-go/v2/cloudstack" |
| corev1 "k8s.io/api/core/v1" |
| ) |
| |
| func TestNodeAddresses(t *testing.T) { |
| cs := &CSCloud{} |
| |
| tests := []struct { |
| name string |
| instance *cloudstack.VirtualMachine |
| wantAddrs []corev1.NodeAddress |
| wantErr bool |
| errContains string |
| }{ |
| { |
| name: "instance with internal IP only", |
| instance: &cloudstack.VirtualMachine{ |
| Id: "vm-1", |
| Name: "test-vm", |
| Nic: []cloudstack.Nic{ |
| {Ipaddress: "10.0.0.1"}, |
| }, |
| }, |
| wantAddrs: []corev1.NodeAddress{ |
| {Type: corev1.NodeInternalIP, Address: "10.0.0.1"}, |
| }, |
| wantErr: false, |
| }, |
| { |
| name: "instance with internal IP and hostname", |
| instance: &cloudstack.VirtualMachine{ |
| Id: "vm-1", |
| Name: "test-vm", |
| Hostname: "test-hostname", |
| Nic: []cloudstack.Nic{ |
| {Ipaddress: "10.0.0.1"}, |
| }, |
| }, |
| wantAddrs: []corev1.NodeAddress{ |
| {Type: corev1.NodeInternalIP, Address: "10.0.0.1"}, |
| {Type: corev1.NodeHostName, Address: "test-hostname"}, |
| }, |
| wantErr: false, |
| }, |
| { |
| name: "instance with internal IP and public IP", |
| instance: &cloudstack.VirtualMachine{ |
| Id: "vm-1", |
| Name: "test-vm", |
| Publicip: "203.0.113.1", |
| Nic: []cloudstack.Nic{ |
| {Ipaddress: "10.0.0.1"}, |
| }, |
| }, |
| wantAddrs: []corev1.NodeAddress{ |
| {Type: corev1.NodeInternalIP, Address: "10.0.0.1"}, |
| {Type: corev1.NodeExternalIP, Address: "203.0.113.1"}, |
| }, |
| wantErr: false, |
| }, |
| { |
| name: "instance with all address types", |
| instance: &cloudstack.VirtualMachine{ |
| Id: "vm-1", |
| Name: "test-vm", |
| Hostname: "test-hostname", |
| Publicip: "203.0.113.1", |
| Nic: []cloudstack.Nic{ |
| {Ipaddress: "10.0.0.1"}, |
| }, |
| }, |
| wantAddrs: []corev1.NodeAddress{ |
| {Type: corev1.NodeInternalIP, Address: "10.0.0.1"}, |
| {Type: corev1.NodeHostName, Address: "test-hostname"}, |
| {Type: corev1.NodeExternalIP, Address: "203.0.113.1"}, |
| }, |
| wantErr: false, |
| }, |
| { |
| name: "instance with no NICs returns error", |
| instance: &cloudstack.VirtualMachine{ |
| Id: "vm-1", |
| Name: "test-vm", |
| Nic: []cloudstack.Nic{}, |
| }, |
| wantAddrs: nil, |
| wantErr: true, |
| errContains: "does not have an internal IP", |
| }, |
| { |
| name: "instance with nil NICs returns error", |
| instance: &cloudstack.VirtualMachine{ |
| Id: "vm-1", |
| Name: "test-vm", |
| Nic: nil, |
| }, |
| wantAddrs: nil, |
| wantErr: true, |
| errContains: "does not have an internal IP", |
| }, |
| { |
| name: "instance with multiple NICs uses first", |
| instance: &cloudstack.VirtualMachine{ |
| Id: "vm-1", |
| Name: "test-vm", |
| Nic: []cloudstack.Nic{ |
| {Ipaddress: "10.0.0.1"}, |
| {Ipaddress: "10.0.0.2"}, |
| }, |
| }, |
| wantAddrs: []corev1.NodeAddress{ |
| {Type: corev1.NodeInternalIP, Address: "10.0.0.1"}, |
| }, |
| wantErr: false, |
| }, |
| } |
| |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| gotAddrs, err := cs.nodeAddresses(tt.instance) |
| |
| if tt.wantErr { |
| if err == nil { |
| t.Errorf("nodeAddresses() expected error, got nil") |
| return |
| } |
| if tt.errContains != "" && !strings.Contains(err.Error(), tt.errContains) { |
| t.Errorf("nodeAddresses() error = %v, want error containing %q", err, tt.errContains) |
| } |
| return |
| } |
| |
| if err != nil { |
| t.Errorf("nodeAddresses() unexpected error: %v", err) |
| return |
| } |
| |
| if len(gotAddrs) != len(tt.wantAddrs) { |
| t.Errorf("nodeAddresses() returned %d addresses, want %d", len(gotAddrs), len(tt.wantAddrs)) |
| return |
| } |
| |
| for i, want := range tt.wantAddrs { |
| if gotAddrs[i].Type != want.Type || gotAddrs[i].Address != want.Address { |
| t.Errorf("nodeAddresses()[%d] = {%v, %v}, want {%v, %v}", |
| i, gotAddrs[i].Type, gotAddrs[i].Address, want.Type, want.Address) |
| } |
| } |
| }) |
| } |
| } |
| |
| func TestGetProviderIDFromInstanceID(t *testing.T) { |
| cs := &CSCloud{} |
| |
| tests := []struct { |
| name string |
| instanceID string |
| want string |
| }{ |
| { |
| name: "valid instance ID", |
| instanceID: "vm-123", |
| want: "external-cloudstack://vm-123", |
| }, |
| { |
| name: "empty instance ID", |
| instanceID: "", |
| want: "external-cloudstack://", |
| }, |
| } |
| |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| got := cs.getProviderIDFromInstanceID(tt.instanceID) |
| if got != tt.want { |
| t.Errorf("getProviderIDFromInstanceID(%q) = %q, want %q", tt.instanceID, got, tt.want) |
| } |
| }) |
| } |
| } |
| |
| func TestGetInstanceIDFromProviderID(t *testing.T) { |
| cs := &CSCloud{} |
| |
| tests := []struct { |
| name string |
| providerID string |
| want string |
| }{ |
| { |
| name: "full provider ID format", |
| providerID: "external-cloudstack://vm-123", |
| want: "vm-123", |
| }, |
| { |
| name: "instance ID only - backward compatibility", |
| providerID: "vm-123", |
| want: "vm-123", |
| }, |
| { |
| name: "empty string", |
| providerID: "", |
| want: "", |
| }, |
| { |
| name: "invalid format - no separator", |
| providerID: "external-cloudstack-vm-123", |
| want: "external-cloudstack-vm-123", |
| }, |
| { |
| name: "different provider prefix", |
| providerID: "aws://i-1234567890abcdef0", |
| want: "i-1234567890abcdef0", |
| }, |
| } |
| |
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| got := cs.getInstanceIDFromProviderID(tt.providerID) |
| if got != tt.want { |
| t.Errorf("getInstanceIDFromProviderID(%q) = %q, want %q", tt.providerID, got, tt.want) |
| } |
| }) |
| } |
| } |