test: basic test

Add basic unit tests.
This commit is contained in:
Serge Logvinov
2023-04-30 13:51:59 +03:00
parent bf10985a6c
commit 90b66dc027
8 changed files with 415 additions and 21 deletions

3
go.mod
View File

@@ -5,6 +5,7 @@ go 1.20
require ( require (
github.com/Telmate/proxmox-api-go v0.0.0-20230329163449-4d08b16c14e0 github.com/Telmate/proxmox-api-go v0.0.0-20230329163449-4d08b16c14e0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.2
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.27.1 k8s.io/api v0.27.1
k8s.io/apimachinery v0.27.1 k8s.io/apimachinery v0.27.1
@@ -58,13 +59,13 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect
github.com/spf13/cobra v1.6.0 // indirect github.com/spf13/cobra v1.6.0 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/stretchr/testify v1.8.2 // indirect
go.etcd.io/etcd/api/v3 v3.5.7 // indirect go.etcd.io/etcd/api/v3 v3.5.7 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect
go.etcd.io/etcd/client/v3 v3.5.7 // indirect go.etcd.io/etcd/client/v3 v3.5.7 // indirect

View File

@@ -1,3 +1,19 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed 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 cluster implements the multi-cloud provider interface for Proxmox. // Package cluster implements the multi-cloud provider interface for Proxmox.
package cluster package cluster
@@ -34,10 +50,6 @@ func NewClient(config *ClustersConfig) (*Client, error) {
client.SetAPIToken(cfg.TokenID, cfg.TokenSecret) client.SetAPIToken(cfg.TokenID, cfg.TokenSecret)
if _, err := client.GetVersion(); err != nil {
return nil, fmt.Errorf("failed to initialized proxmox client in cluster %s: %v", cfg.Region, err)
}
proxmox[cfg.Region] = client proxmox[cfg.Region] = client
} }
@@ -47,7 +59,7 @@ func NewClient(config *ClustersConfig) (*Client, error) {
}, nil }, nil
} }
return nil, nil return nil, fmt.Errorf("no Proxmox clusters found")
} }
// CheckClusters checks if the Proxmox connection is working. // CheckClusters checks if the Proxmox connection is working.

View File

@@ -0,0 +1,76 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed 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 cluster
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func newClientEnv() (*ClustersConfig, error) {
cfg, err := ReadCloudConfig(strings.NewReader(`
clusters:
- url: https://127.0.0.1:8006
insecure: false
token_id: "user!token-id"
token_secret: "secret"
region: cluster-1
`))
return &cfg, err
}
func TestNewClient(t *testing.T) {
cfg, err := newClientEnv()
assert.Nil(t, err)
assert.NotNil(t, cfg)
client, err := NewClient(&ClustersConfig{})
assert.NotNil(t, err)
assert.Nil(t, client)
client, err = NewClient(cfg)
assert.Nil(t, err)
assert.NotNil(t, client)
assert.Equal(t, 1, len(client.proxmox))
}
func TestCheckClusters(t *testing.T) {
cfg, err := newClientEnv()
assert.Nil(t, err)
assert.NotNil(t, cfg)
client, err := NewClient(cfg)
assert.Nil(t, err)
assert.NotNil(t, client)
assert.Equal(t, 1, len(client.proxmox))
pxapi, err := client.GetProxmoxCluster("test")
assert.NotNil(t, err)
assert.Nil(t, pxapi)
assert.Equal(t, "proxmox cluster test not found", err.Error())
pxapi, err = client.GetProxmoxCluster("cluster-1")
assert.Nil(t, err)
assert.NotNil(t, pxapi)
err = client.CheckClusters()
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "failed to initialized proxmox client in region")
}

View File

@@ -1,3 +1,19 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed 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 cluster package cluster
import ( import (
@@ -41,13 +57,5 @@ func ReadCloudConfigFromFile(file string) (ClustersConfig, error) {
} }
defer f.Close() // nolint: errcheck defer f.Close() // nolint: errcheck
cfg := ClustersConfig{} return ReadCloudConfig(f)
if f != nil {
if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
return ClustersConfig{}, err
}
}
return cfg, nil
} }

View File

@@ -0,0 +1,71 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed 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 cluster
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestReadCloudConfig(t *testing.T) {
cfg, err := ReadCloudConfig(nil)
assert.Nil(t, err)
assert.NotNil(t, cfg)
// Empty config
cfg, err = ReadCloudConfig(strings.NewReader(`
clusters:
`))
assert.Nil(t, err)
assert.NotNil(t, cfg)
// Wrong config
cfg, err = ReadCloudConfig(strings.NewReader(`
clusters:
test: false
`))
assert.NotNil(t, err)
assert.NotNil(t, cfg)
// Valid config with one cluster
cfg, err = ReadCloudConfig(strings.NewReader(`
clusters:
- url: https://example.com
insecure: false
token_id: "user!token-id"
token_secret: "secret"
region: cluster-1
`))
assert.Nil(t, err)
assert.NotNil(t, cfg)
assert.Equal(t, 1, len(cfg.Clusters))
}
func TestReadCloudConfigFromFile(t *testing.T) {
cfg, err := ReadCloudConfigFromFile("testdata/cloud-config.yaml")
assert.NotNil(t, err)
assert.EqualError(t, err, "error reading testdata/cloud-config.yaml: open testdata/cloud-config.yaml: no such file or directory")
assert.NotNil(t, cfg)
cfg, err = ReadCloudConfigFromFile("../../hack/proxmox-config.yaml")
assert.Nil(t, err)
assert.NotNil(t, cfg)
assert.Equal(t, 2, len(cfg.Clusters))
}

80
pkg/proxmox/cloud_test.go Normal file
View File

@@ -0,0 +1,80 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed 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 proxmox
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/sergelogvinov/proxmox-cloud-controller-manager/pkg/cluster"
)
func TestNewCloudError(t *testing.T) {
cloud, err := newCloud(&cluster.ClustersConfig{})
assert.NotNil(t, err)
assert.Nil(t, cloud)
assert.EqualError(t, err, "no Proxmox clusters found")
}
func TestCloud(t *testing.T) {
cfg, err := cluster.ReadCloudConfig(strings.NewReader(`
clusters:
- url: https://example.com
insecure: false
token_id: "user!token-id"
token_secret: "secret"
region: cluster-1
`))
assert.Nil(t, err)
assert.NotNil(t, cfg)
cloud, err := newCloud(&cfg)
assert.Nil(t, err)
assert.NotNil(t, cloud)
lb, res := cloud.LoadBalancer()
assert.Nil(t, lb)
assert.Equal(t, res, false)
ins, res := cloud.Instances()
assert.Nil(t, ins)
assert.Equal(t, res, false)
ins2, res := cloud.InstancesV2()
assert.NotNil(t, ins2)
assert.Equal(t, res, true)
zone, res := cloud.Zones()
assert.Nil(t, zone)
assert.Equal(t, res, false)
cl, res := cloud.Clusters()
assert.Nil(t, cl)
assert.Equal(t, res, false)
route, res := cloud.Routes()
assert.Nil(t, route)
assert.Equal(t, res, false)
pName := cloud.ProviderName()
assert.Equal(t, pName, ProviderName)
clID := cloud.HasClusterID()
assert.Equal(t, clID, true)
}

View File

@@ -1,3 +1,19 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed 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 proxmox package proxmox
import ( import (
@@ -141,10 +157,6 @@ func (i *instances) InstanceMetadata(_ context.Context, node *v1.Node) (*cloudpr
return &cloudprovider.InstanceMetadata{}, nil return &cloudprovider.InstanceMetadata{}, nil
} }
func (i *instances) getProviderID(region string, vmr *pxapi.VmRef) string {
return fmt.Sprintf("%s://%s/%d", ProviderName, region, vmr.VmId())
}
func (i *instances) getInstance(node *v1.Node) (*pxapi.VmRef, string, error) { func (i *instances) getInstance(node *v1.Node) (*pxapi.VmRef, string, error) {
if !strings.HasPrefix(node.Spec.ProviderID, ProviderName) { if !strings.HasPrefix(node.Spec.ProviderID, ProviderName) {
klog.V(4).Infof("instances.getInstance() node %s has foreign providerID: %s, skipped", node.Name, node.Spec.ProviderID) klog.V(4).Infof("instances.getInstance() node %s has foreign providerID: %s, skipped", node.Name, node.Spec.ProviderID)
@@ -194,15 +206,23 @@ func (i *instances) getInstanceType(vmRef *pxapi.VmRef, region string) (string,
var providerIDRegexp = regexp.MustCompile(`^` + ProviderName + `://([^/]*)/([^/]+)$`) var providerIDRegexp = regexp.MustCompile(`^` + ProviderName + `://([^/]*)/([^/]+)$`)
func (i *instances) getProviderID(region string, vmr *pxapi.VmRef) string {
return fmt.Sprintf("%s://%s/%d", ProviderName, region, vmr.VmId())
}
func (i *instances) parseProviderID(providerID string) (*pxapi.VmRef, string, error) { func (i *instances) parseProviderID(providerID string) (*pxapi.VmRef, string, error) {
if !strings.HasPrefix(providerID, ProviderName) {
return nil, "", fmt.Errorf("foreign providerID or empty \"%s\"", providerID)
}
matches := providerIDRegexp.FindStringSubmatch(providerID) matches := providerIDRegexp.FindStringSubmatch(providerID)
if len(matches) != 3 { if len(matches) != 3 {
return nil, "", fmt.Errorf("ProviderID \"%s\" didn't match expected format \"%s://region/InstanceID\"", providerID, ProviderName) return nil, "", fmt.Errorf("providerID \"%s\" didn't match expected format \"%s://region/InstanceID\"", providerID, ProviderName)
} }
vmID, err := strconv.Atoi(matches[2]) vmID, err := strconv.Atoi(matches[2])
if err != nil { if err != nil {
return nil, "", err return nil, "", fmt.Errorf("providerID \"%s\" didn't match expected format \"%s://region/InstanceID\"", providerID, ProviderName)
} }
return pxapi.NewVmRef(vmID), matches[1], nil return pxapi.NewVmRef(vmID), matches[1], nil

View File

@@ -0,0 +1,126 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed 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 proxmox
import (
"fmt"
"testing"
pxapi "github.com/Telmate/proxmox-api-go/proxmox"
"github.com/stretchr/testify/assert"
)
func TestGetProviderID(t *testing.T) {
t.Parallel()
i := newInstances(nil)
tests := []struct {
msg string
region string
vmr *pxapi.VmRef
expected string
}{
{
msg: "empty region",
region: "",
vmr: pxapi.NewVmRef(100),
expected: "proxmox:///100",
},
{
msg: "region",
region: "cluster1",
vmr: pxapi.NewVmRef(100),
expected: "proxmox://cluster1/100",
},
}
for _, testCase := range tests {
testCase := testCase
t.Run(fmt.Sprint(testCase.msg), func(t *testing.T) {
t.Parallel()
expected := i.getProviderID(testCase.region, testCase.vmr)
assert.Equal(t, expected, testCase.expected)
})
}
}
func TestParseProviderID(t *testing.T) {
t.Parallel()
i := newInstances(nil)
tests := []struct {
msg string
magic string
expectedCluster string
expectedVmr *pxapi.VmRef
expectedError error
}{
{
msg: "Empty magic string",
magic: "",
expectedError: fmt.Errorf("foreign providerID or empty \"\""),
},
{
msg: "Wrong provider",
magic: "provider://region/100",
expectedError: fmt.Errorf("foreign providerID or empty \"provider://region/100\""),
},
{
msg: "Empty region",
magic: "proxmox:///100",
expectedCluster: "",
expectedVmr: pxapi.NewVmRef(100),
},
{
msg: "Empty region",
magic: "proxmox://100",
expectedError: fmt.Errorf("providerID \"proxmox://100\" didn't match expected format \"proxmox://region/InstanceID\""),
},
{
msg: "Cluster and InstanceID",
magic: "proxmox://cluster/100",
expectedCluster: "cluster",
expectedVmr: pxapi.NewVmRef(100),
},
{
msg: "Cluster and wrong InstanceID",
magic: "proxmox://cluster/name",
expectedError: fmt.Errorf("providerID \"proxmox://cluster/name\" didn't match expected format \"proxmox://region/InstanceID\""),
},
}
for _, testCase := range tests {
testCase := testCase
t.Run(fmt.Sprint(testCase.msg), func(t *testing.T) {
t.Parallel()
vmr, cluster, err := i.parseProviderID(testCase.magic)
if testCase.expectedError != nil {
assert.Equal(t, testCase.expectedError, err)
} else {
assert.Equal(t, testCase.expectedVmr, vmr)
assert.Equal(t, testCase.expectedCluster, cluster)
}
})
}
}