200 lines
5.8 KiB
Go
200 lines
5.8 KiB
Go
/*
|
|
Copyright 2022 DigitalOcean
|
|
|
|
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 kube
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
|
|
csi "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/fake"
|
|
"github.com/stretchr/testify/assert"
|
|
corev1 "k8s.io/api/core/v1"
|
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/client-go/kubernetes/fake"
|
|
ktesting "k8s.io/client-go/testing"
|
|
)
|
|
|
|
func TestFetchObjects(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
fakeMutator func(cs *fake.Clientset)
|
|
}{
|
|
{
|
|
name: "happy path",
|
|
},
|
|
{
|
|
name: "resources not found",
|
|
fakeMutator: func(cs *fake.Clientset) {
|
|
notFoundReactionFunc := func(action ktesting.Action) (bool, runtime.Object, error) {
|
|
return true, nil, &kerrors.StatusError{
|
|
ErrStatus: metav1.Status{
|
|
Reason: metav1.StatusReasonNotFound,
|
|
Message: fmt.Sprintf("%s not found", action.GetResource().Resource),
|
|
},
|
|
}
|
|
}
|
|
cs.PrependReactor("list", "mutatingwebhookconfigurations", notFoundReactionFunc)
|
|
cs.PrependReactor("list", "validatingwebhookconfigurations", notFoundReactionFunc)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
cs := fake.NewSimpleClientset()
|
|
csifake := csi.NewSimpleClientset()
|
|
if test.fakeMutator != nil {
|
|
test.fakeMutator(cs)
|
|
}
|
|
|
|
api := &Client{
|
|
KubeClient: cs,
|
|
CSIClient: csifake,
|
|
}
|
|
|
|
api.KubeClient.CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{
|
|
TypeMeta: metav1.TypeMeta{Kind: "Namespace", APIVersion: "v1"},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "kube-system",
|
|
Labels: map[string]string{"doks_key": "bar"}},
|
|
}, metav1.CreateOptions{})
|
|
|
|
actual, err := api.FetchObjects(context.Background(), ObjectFilter{})
|
|
assert.NoError(t, err)
|
|
|
|
assert.NotNil(t, actual.Nodes)
|
|
assert.NotNil(t, actual.PersistentVolumes)
|
|
assert.NotNil(t, actual.Pods)
|
|
assert.NotNil(t, actual.PodTemplates)
|
|
assert.NotNil(t, actual.PersistentVolumeClaims)
|
|
assert.NotNil(t, actual.ConfigMaps)
|
|
assert.NotNil(t, actual.Services)
|
|
assert.NotNil(t, actual.Secrets)
|
|
assert.NotNil(t, actual.ServiceAccounts)
|
|
assert.NotNil(t, actual.ResourceQuotas)
|
|
assert.NotNil(t, actual.LimitRanges)
|
|
assert.NotNil(t, actual.ValidatingWebhookConfigurations)
|
|
assert.NotNil(t, actual.MutatingWebhookConfigurations)
|
|
assert.NotNil(t, actual.SystemNamespace)
|
|
assert.NotNil(t, actual.CronJobs)
|
|
assert.NotNil(t, actual.VolumeSnapshotsV1)
|
|
assert.NotNil(t, actual.VolumeSnapshotsBeta)
|
|
assert.NotNil(t, actual.VolumeSnapshotsV1Content)
|
|
assert.NotNil(t, actual.VolumeSnapshotsBetaContent)
|
|
}
|
|
|
|
}
|
|
|
|
func TestNewClientErrors(t *testing.T) {
|
|
t.Run("both yaml and filepath specified", func(t *testing.T) {
|
|
_, err := NewClient(WithConfigFile("some-path"), WithYaml([]byte("yaml")))
|
|
assert.Equal(t, errors.New("cannot specify yaml and kubeconfig file paths"), err)
|
|
})
|
|
|
|
t.Run("both yaml and KUBECONFIG specified", func(t *testing.T) {
|
|
_, err := NewClient(WithMergedConfigFiles([]string{"some-path"}), WithYaml([]byte("yaml")))
|
|
assert.Equal(t, errors.New("cannot specify yaml and kubeconfig file paths"), err)
|
|
})
|
|
|
|
t.Run("both yaml and in-cluster specified", func(t *testing.T) {
|
|
_, err := NewClient(WithYaml([]byte("yaml")), InCluster())
|
|
assert.Equal(t, errors.New("cannot specify yaml or kubeconfig file paths when running in-cluster mode"), err)
|
|
})
|
|
|
|
t.Run("both KUBECONFIG and in-cluster specified", func(t *testing.T) {
|
|
_, err := NewClient(WithMergedConfigFiles([]string{"some-path"}), InCluster())
|
|
assert.Equal(t, errors.New("cannot specify yaml or kubeconfig file paths when running in-cluster mode"), err)
|
|
})
|
|
|
|
t.Run("in-cluster access enabled", func(t *testing.T) {
|
|
_, err := NewClient(InCluster())
|
|
assert.Equal(t, errors.New("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined"), err)
|
|
})
|
|
}
|
|
|
|
type failTransport struct{}
|
|
|
|
func (failTransport) RoundTrip(_ *http.Request) (*http.Response, error) {
|
|
return nil, errors.New("fail")
|
|
}
|
|
|
|
func TestNewClientRoundTripper(t *testing.T) {
|
|
client, err := NewClient(WithTransportWrapper(func(_ http.RoundTripper) http.RoundTripper {
|
|
return failTransport{}
|
|
}), WithYaml([]byte(`apiVersion: v1
|
|
kind: Config
|
|
clusters:
|
|
- cluster:
|
|
server: http://localhost
|
|
name: cool
|
|
contexts:
|
|
- context:
|
|
cluster: cool
|
|
user: admin
|
|
name: cool
|
|
current-context: cool
|
|
users:
|
|
- name: admin
|
|
`)))
|
|
assert.NoError(t, err)
|
|
_, err = client.KubeClient.CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{
|
|
TypeMeta: metav1.TypeMeta{Kind: "Namespace", APIVersion: "v1"},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "kube-system",
|
|
},
|
|
}, metav1.CreateOptions{})
|
|
assert.Contains(t, err.Error(), "fail")
|
|
}
|
|
|
|
func TestAnnotateFetchError(t *testing.T) {
|
|
kindName := "kind"
|
|
|
|
tests := []struct {
|
|
name string
|
|
inErr error
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "no error",
|
|
inErr: nil,
|
|
wantErr: nil,
|
|
},
|
|
{
|
|
name: "not found error",
|
|
inErr: &kerrors.StatusError{
|
|
ErrStatus: metav1.Status{
|
|
Reason: metav1.StatusReasonNotFound,
|
|
},
|
|
},
|
|
wantErr: nil,
|
|
},
|
|
{
|
|
name: "other error",
|
|
inErr: errors.New("other error"),
|
|
wantErr: fmt.Errorf("failed to fetch %s: other error", kindName),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
assert.Equal(t, test.wantErr, annotateFetchError(kindName, test.inErr))
|
|
}
|
|
}
|