Files
velero/pkg/backup/remap_crd_version_action_test.go
Bridget McErlean b090b27275 Cherry-pick and update changelog for v1.6.3 (#4018)
* Use appropriate CRD API during readiness check (#4015)

* Use appropriate CRD API during readiness check

The readiness check for the Velero CRDs was still using the v1beta1 API.
This would cause the readiness check to fail on 1.22 clusters as the
v1beta1 API is no longer available. Previously, this error would be
ignored and the installation would proceed, however with #4002, we are
no longer ignoring errors from this check.

This change modifies the CRD readiness check to check the CRDs using the
same API version that was used when submitting the CRDs to the cluster.
It also introduces a new CRD builder using the V1 API for testing.

This change also fixes a bug that was identified in the polling code
where if the CRDs were not ready on the first polling iteration, they
would be added again to the list of CRDs to check resulting in
duplicates. This would cause the length check to fail on all subsequent
polls and the timeout would always be reached.

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>

* Remove duplicate V1 CRD builder and update comment

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>

* Merge pull request #4012 from jenting/add-k8s-1.22-ci-test

Add Kubernetes v1.22 CI test

* Update changelog for v1.6.3

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>

Co-authored-by: Scott Seago <sseago@redhat.com>
2021-08-06 22:49:34 +08:00

195 lines
7.2 KiB
Go

/*
Copyright the Velero contributors.
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 backup
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiextfakes "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)
func TestRemapCRDVersionAction(t *testing.T) {
backup := &v1.Backup{}
clientset := apiextfakes.NewSimpleClientset()
betaClient := clientset.ApiextensionsV1beta1().CustomResourceDefinitions()
// build a v1beta1 CRD with the same name and add it to the fake client that the plugin is going to call.
// keep the same one for all 3 tests, since there's little value in recreating it
b := builder.ForCustomResourceDefinitionV1Beta1("test.velero.io")
c := b.Result()
_, err := betaClient.Create(context.TODO(), c, metav1.CreateOptions{})
require.NoError(t, err)
a := NewRemapCRDVersionAction(velerotest.NewLogger(), betaClient)
t.Run("Test a v1 CRD without any Schema information", func(t *testing.T) {
b := builder.ForV1CustomResourceDefinition("test.velero.io")
// Set a version that does not include and schema information.
b.Version(builder.ForV1CustomResourceDefinitionVersion("v1").Served(true).Storage(true).Result())
c := b.Result()
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&c)
require.NoError(t, err)
// Execute the plugin, which will call the fake client
item, _, err := a.Execute(&unstructured.Unstructured{Object: obj}, backup)
require.NoError(t, err)
assert.Equal(t, "apiextensions.k8s.io/v1beta1", item.UnstructuredContent()["apiVersion"])
})
t.Run("Test a v1 CRD with a NonStructuralSchema Condition", func(t *testing.T) {
b := builder.ForV1CustomResourceDefinition("test.velero.io")
b.Condition(builder.ForV1CustomResourceDefinitionCondition().Type(apiextv1.NonStructuralSchema).Result())
c := b.Result()
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&c)
require.NoError(t, err)
item, _, err := a.Execute(&unstructured.Unstructured{Object: obj}, backup)
require.NoError(t, err)
assert.Equal(t, "apiextensions.k8s.io/v1beta1", item.UnstructuredContent()["apiVersion"])
})
t.Run("Having an integer on a float64 field should work (issue 2319)", func(t *testing.T) {
b := builder.ForV1CustomResourceDefinition("test.velero.io")
// 5 here is just an int value, it could be any other whole number.
schema := builder.ForJSONSchemaPropsBuilder().Maximum(5).Result()
b.Version(builder.ForV1CustomResourceDefinitionVersion("v1").Served(true).Storage(true).Schema(schema).Result())
c := b.Result()
// Marshall in and out of JSON because the problem doesn't manifest when we use ToUnstructured directly
// This should simulate the JSON passing over the wire in an HTTP request/response with a dynamic client
js, err := json.Marshal(c)
require.NoError(t, err)
var u unstructured.Unstructured
err = json.Unmarshal(js, &u)
require.NoError(t, err)
_, _, err = a.Execute(&u, backup)
require.NoError(t, err)
})
t.Run("Having Spec.PreserveUnknownFields set to true will return a v1beta1 version of the CRD", func(t *testing.T) {
b := builder.ForV1CustomResourceDefinition("test.velero.io")
b.PreserveUnknownFields(true)
c := b.Result()
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&c)
require.NoError(t, err)
item, _, err := a.Execute(&unstructured.Unstructured{Object: obj}, backup)
require.NoError(t, err)
assert.Equal(t, "apiextensions.k8s.io/v1beta1", item.UnstructuredContent()["apiVersion"])
})
}
// TestRemapCRDVersionActionData tests the RemapCRDVersionAction plugin against actual CRD to confirm that the v1beta1 version is returned when the v1 version is passed in to the plugin.
func TestRemapCRDVersionActionData(t *testing.T) {
backup := &v1.Backup{}
clientset := apiextfakes.NewSimpleClientset()
betaClient := clientset.ApiextensionsV1beta1().CustomResourceDefinitions()
a := NewRemapCRDVersionAction(velerotest.NewLogger(), betaClient)
tests := []struct {
crd string
expectAdditionalColumns bool
}{
{
crd: "elasticsearches.elasticsearch.k8s.elastic.co",
expectAdditionalColumns: true,
},
{
crd: "kibanas.kibana.k8s.elastic.co",
expectAdditionalColumns: true,
},
{
crd: "gcpsamples.gcp.stacks.crossplane.io",
},
{
crd: "alertmanagers.monitoring.coreos.com",
},
{
crd: "prometheuses.monitoring.coreos.com",
},
}
for _, test := range tests {
tName := fmt.Sprintf("%s CRD passed in as v1 should be returned as v1beta1", test.crd)
t.Run(tName, func(t *testing.T) {
// We don't need a Go struct of the v1 data, just an unstructured to pass into the plugin.
v1File := fmt.Sprintf("testdata/v1/%s.json", test.crd)
f, err := ioutil.ReadFile(v1File)
require.NoError(t, err)
var obj unstructured.Unstructured
err = json.Unmarshal([]byte(f), &obj)
require.NoError(t, err)
// Load a v1beta1 struct into the beta client to be returned
v1beta1File := fmt.Sprintf("testdata/v1beta1/%s.json", test.crd)
f, err = ioutil.ReadFile(v1beta1File)
require.NoError(t, err)
var crd apiextv1beta1.CustomResourceDefinition
err = json.Unmarshal([]byte(f), &crd)
require.NoError(t, err)
_, err = betaClient.Create(context.TODO(), &crd, metav1.CreateOptions{})
require.NoError(t, err)
// Run method under test
item, _, err := a.Execute(&obj, backup)
require.NoError(t, err)
assert.Equal(t, "apiextensions.k8s.io/v1beta1", item.UnstructuredContent()["apiVersion"])
assert.Equal(t, crd.Kind, item.GetObjectKind().GroupVersionKind().GroupKind().Kind)
name, _, err := unstructured.NestedString(item.UnstructuredContent(), "metadata", "name")
require.NoError(t, err)
assert.Equal(t, crd.Name, name)
uid, _, err := unstructured.NestedString(item.UnstructuredContent(), "metadata", "uid")
require.NoError(t, err)
assert.Equal(t, string(crd.UID), uid)
// For ElasticSearch and Kibana, problems manifested when additionalPrinterColumns was moved from the top-level spec down to the
// versions slice.
if test.expectAdditionalColumns {
_, ok := item.UnstructuredContent()["spec"].(map[string]interface{})["additionalPrinterColumns"]
assert.True(t, ok)
}
// Clean up the item created in the test.
betaClient.Delete(context.TODO(), crd.Name, metav1.DeleteOptions{})
})
}
}