Reduce CRD size.
1. Make the Restore hook.InitConatianer server side field pruing disable. 2. Remove restore patch in update-generate-crd-code.sh. 3. Modify related testcases. 4. Add Container fields validation in Restore Init hook. Signed-off-by: Xun Jiang <jxun@vmware.com>pull/5174/head
parent
108c81d84c
commit
e8da5df57a
|
@ -0,0 +1 @@
|
|||
Reduce CRD size.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,3 +0,0 @@
|
|||
[
|
||||
{ "op": "replace", "path": "/spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/hooks/properties/resources/items/properties/postHooks/items/properties/init/properties/initContainers/items/properties/ports/items/required", "value": [ "containerPort", "protocol"] }
|
||||
]
|
|
@ -1,3 +0,0 @@
|
|||
[
|
||||
{ "op": "replace", "path": "/spec/validation/openAPIV3Schema/properties/spec/properties/hooks/properties/resources/items/properties/postHooks/items/properties/init/properties/initContainers/items/properties/ports/items/required", "value": [ "containerPort", "protocol"] }
|
||||
]
|
|
@ -47,7 +47,7 @@ ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \
|
|||
# Generate apiextensions.k8s.io/v1
|
||||
# Generate manifests e.g. CRD, RBAC etc.
|
||||
controller-gen \
|
||||
crd:crdVersions=v1\
|
||||
crd:crdVersions=v1 \
|
||||
paths=./pkg/apis/velero/v1/... \
|
||||
rbac:roleName=velero-perms \
|
||||
paths=./pkg/controller/... \
|
||||
|
@ -55,13 +55,4 @@ controller-gen \
|
|||
object \
|
||||
paths=./pkg/apis/velero/v1/...
|
||||
|
||||
# this is a super hacky workaround for https://github.com/kubernetes/kubernetes/issues/91395
|
||||
# which a result of fixing the validation on CRD objects. The validation ensures the fields that are list map keys, are either marked
|
||||
# as required or have default values to ensure merging of list map items work as expected.
|
||||
# With "containerPort" and "protocol" being considered as x-kubernetes-list-map-keys in the container ports, and "protocol" was not
|
||||
# a required field, the CRD would fail validation with errors similar to the one reported in https://github.com/kubernetes/kubernetes/issues/91395.
|
||||
# once controller-gen (above) is able to generate CRDs with `protocol` as a required field, this hack can be removed.
|
||||
kubectl patch -f config/crd/v1/bases/velero.io_restores.yaml -p "$(cat hack/restore-crd-patch-v1.json)" --type=json --local=true -o yaml > /tmp/velero.io_restores-yaml.patched
|
||||
mv /tmp/velero.io_restores-yaml.patched config/crd/v1/bases/velero.io_restores.yaml
|
||||
|
||||
go generate ./config/crd/v1/crds
|
||||
|
|
|
@ -131,10 +131,10 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks(
|
|||
pod.Spec.InitContainers = pod.Spec.InitContainers[1:]
|
||||
}
|
||||
|
||||
hooksFromAnnotations := getInitRestoreHookFromAnnotation(kube.NamespaceAndName(pod), metadata.GetAnnotations(), log)
|
||||
if hooksFromAnnotations != nil {
|
||||
initContainerFromAnnotations := getInitContainerFromAnnotation(kube.NamespaceAndName(pod), metadata.GetAnnotations(), log)
|
||||
if initContainerFromAnnotations != nil {
|
||||
log.Infof("Handling InitRestoreHooks from pod annotations")
|
||||
initContainers = append(initContainers, hooksFromAnnotations.InitContainers...)
|
||||
initContainers = append(initContainers, *initContainerFromAnnotations)
|
||||
} else {
|
||||
log.Infof("Handling InitRestoreHooks from RestoreSpec")
|
||||
// pod did not have the annotations appropriate for restore hooks
|
||||
|
@ -155,7 +155,22 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks(
|
|||
}
|
||||
for _, hook := range rh.RestoreHooks {
|
||||
if hook.Init != nil {
|
||||
initContainers = append(initContainers, hook.Init.InitContainers...)
|
||||
containers := make([]corev1api.Container, 0)
|
||||
for _, raw := range hook.Init.InitContainers {
|
||||
container := corev1api.Container{}
|
||||
err := ValidateContainer(raw.Raw)
|
||||
if err != nil {
|
||||
log.Errorf("invalid Restore Init hook: %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(raw.Raw, &container)
|
||||
if err != nil {
|
||||
log.Errorf("fail to Unmarshal hook Init into container: %s", err.Error())
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
containers = append(containers, container)
|
||||
}
|
||||
initContainers = append(initContainers, containers...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -350,7 +365,7 @@ type ResourceRestoreHook struct {
|
|||
RestoreHooks []velerov1api.RestoreResourceHook
|
||||
}
|
||||
|
||||
func getInitRestoreHookFromAnnotation(podName string, annotations map[string]string, log logrus.FieldLogger) *velerov1api.InitRestoreHook {
|
||||
func getInitContainerFromAnnotation(podName string, annotations map[string]string, log logrus.FieldLogger) *corev1api.Container {
|
||||
containerImage := annotations[podRestoreHookInitContainerImageAnnotationKey]
|
||||
containerName := annotations[podRestoreHookInitContainerNameAnnotationKey]
|
||||
command := annotations[podRestoreHookInitContainerCommandAnnotationKey]
|
||||
|
@ -373,15 +388,13 @@ func getInitRestoreHookFromAnnotation(podName string, annotations map[string]str
|
|||
log.Infof("Pod %s has no %s annotation, using generated name %s for initContainer", podName, podRestoreHookInitContainerNameAnnotationKey, containerName)
|
||||
}
|
||||
|
||||
return &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Image: containerImage,
|
||||
Name: containerName,
|
||||
Command: parseStringToCommand(command),
|
||||
},
|
||||
},
|
||||
initContainer := corev1api.Container{
|
||||
Image: containerImage,
|
||||
Name: containerName,
|
||||
Command: parseStringToCommand(command),
|
||||
}
|
||||
|
||||
return &initContainer
|
||||
}
|
||||
|
||||
// GetRestoreHooksFromSpec returns a list of ResourceRestoreHooks from the restore Spec.
|
||||
|
@ -406,7 +419,7 @@ func GetRestoreHooksFromSpec(hooksSpec *velerov1api.RestoreHooks) ([]ResourceRes
|
|||
if rs.LabelSelector != nil {
|
||||
ls, err := metav1.LabelSelectorAsSelector(rs.LabelSelector)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
return []ResourceRestoreHook{}, errors.WithStack(err)
|
||||
}
|
||||
rh.Selector.LabelSelector = ls
|
||||
}
|
||||
|
@ -526,3 +539,17 @@ func GroupRestoreExecHooks(
|
|||
|
||||
return byContainer, nil
|
||||
}
|
||||
|
||||
// ValidateContainer validate whether a map contains mandatory k8s Container fields.
|
||||
// mandatory fields include name, image and commands.
|
||||
func ValidateContainer(raw []byte) error {
|
||||
container := corev1api.Container{}
|
||||
err := json.Unmarshal(raw, &container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(container.Command) <= 0 || len(container.Name) <= 0 || len(container.Image) <= 0 {
|
||||
return fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1191,11 +1191,11 @@ func TestGroupRestoreExecHooks(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetInitRestoreHookFromAnnotations(t *testing.T) {
|
||||
func TestGetInitContainerFromAnnotations(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
inputAnnotations map[string]string
|
||||
expected velerov1api.InitRestoreHook
|
||||
expected *corev1api.Container
|
||||
expectNil bool
|
||||
}{
|
||||
{
|
||||
|
@ -1223,12 +1223,8 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) {
|
|||
podRestoreHookInitContainerNameAnnotationKey: "",
|
||||
podRestoreHookInitContainerCommandAnnotationKey: "/usr/bin/data-populator /user-data full",
|
||||
},
|
||||
expected: velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"/usr/bin/data-populator /user-data full"}).Result(),
|
||||
},
|
||||
},
|
||||
expected: builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"/usr/bin/data-populator /user-data full"}).Result(),
|
||||
},
|
||||
{
|
||||
name: "should generate container name when container name is missing",
|
||||
|
@ -1237,22 +1233,14 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) {
|
|||
podRestoreHookInitContainerImageAnnotationKey: "busy-box",
|
||||
podRestoreHookInitContainerCommandAnnotationKey: "/usr/bin/data-populator /user-data full",
|
||||
},
|
||||
expected: velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"/usr/bin/data-populator /user-data full"}).Result(),
|
||||
},
|
||||
},
|
||||
expected: builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"/usr/bin/data-populator /user-data full"}).Result(),
|
||||
},
|
||||
{
|
||||
name: "should return expected init container when all annotations are specified",
|
||||
expectNil: false,
|
||||
expected: velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"/usr/bin/data-populator /user-data full"}).Result(),
|
||||
},
|
||||
},
|
||||
expected: builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"/usr/bin/data-populator /user-data full"}).Result(),
|
||||
inputAnnotations: map[string]string{
|
||||
podRestoreHookInitContainerImageAnnotationKey: "busy-box",
|
||||
podRestoreHookInitContainerNameAnnotationKey: "restore-init",
|
||||
|
@ -1262,12 +1250,8 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) {
|
|||
{
|
||||
name: "should return expected init container when all annotations are specified with command as a JSON array",
|
||||
expectNil: false,
|
||||
expected: velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
},
|
||||
},
|
||||
expected: builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
inputAnnotations: map[string]string{
|
||||
podRestoreHookInitContainerImageAnnotationKey: "busy-box",
|
||||
podRestoreHookInitContainerNameAnnotationKey: "restore-init",
|
||||
|
@ -1277,12 +1261,8 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) {
|
|||
{
|
||||
name: "should return expected init container when all annotations are specified with command as malformed a JSON array",
|
||||
expectNil: false,
|
||||
expected: velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"[foobarbaz"}).Result(),
|
||||
},
|
||||
},
|
||||
expected: builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"[foobarbaz"}).Result(),
|
||||
inputAnnotations: map[string]string{
|
||||
podRestoreHookInitContainerImageAnnotationKey: "busy-box",
|
||||
podRestoreHookInitContainerNameAnnotationKey: "restore-init",
|
||||
|
@ -1293,15 +1273,14 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) {
|
|||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual := getInitRestoreHookFromAnnotation("test/pod1", tc.inputAnnotations, velerotest.NewLogger())
|
||||
actualInitContainer := getInitContainerFromAnnotation("test/pod1", tc.inputAnnotations, velerotest.NewLogger())
|
||||
if tc.expectNil {
|
||||
assert.Nil(t, actual)
|
||||
assert.Nil(t, actualInitContainer)
|
||||
return
|
||||
}
|
||||
assert.NotEmpty(t, actual.InitContainers[0].Name)
|
||||
assert.Equal(t, len(tc.expected.InitContainers), len(actual.InitContainers))
|
||||
assert.Equal(t, tc.expected.InitContainers[0].Image, actual.InitContainers[0].Image)
|
||||
assert.Equal(t, tc.expected.InitContainers[0].Command, actual.InitContainers[0].Command)
|
||||
assert.NotEmpty(t, actualInitContainer.Name)
|
||||
assert.Equal(t, tc.expected.Image, actualInitContainer.Image)
|
||||
assert.Equal(t, tc.expected.Command, actualInitContainer.Command)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1347,11 +1326,11 @@ func TestGetRestoreHooksFromSpec(t *testing.T) {
|
|||
PostHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"foobarbaz"}).Result(),
|
||||
*builder.ForContainer("restore-init2", "busy-box").
|
||||
Command([]string{"foobarbaz"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"foobarbaz"}).ResultRawExtension(),
|
||||
builder.ForContainer("restore-init2", "busy-box").
|
||||
Command([]string{"foobarbaz"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1369,11 +1348,11 @@ func TestGetRestoreHooksFromSpec(t *testing.T) {
|
|||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"foobarbaz"}).Result(),
|
||||
*builder.ForContainer("restore-init2", "busy-box").
|
||||
Command([]string{"foobarbaz"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"foobarbaz"}).ResultRawExtension(),
|
||||
builder.ForContainer("restore-init2", "busy-box").
|
||||
Command([]string{"foobarbaz"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1539,9 +1518,9 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("should-not exist", "does-not-matter").
|
||||
Command([]string{""}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("should-not exist", "does-not-matter").
|
||||
Command([]string{""}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1556,6 +1535,9 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
Name: "app1",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{},
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
expectedPod: &corev1api.Pod{
|
||||
|
@ -1582,11 +1564,11 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
*builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1643,11 +1625,11 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
*builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1680,11 +1662,11 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
*builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1733,11 +1715,11 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
*builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1795,11 +1777,11 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
*builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
builder.ForContainer("restore-init-container-2", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1868,9 +1850,9 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1911,9 +1893,9 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init-container-1", "nginx").
|
||||
Command([]string{"a", "b", "c"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1922,6 +1904,37 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
},
|
||||
namespaceMapping: map[string]string{"default": "new"},
|
||||
},
|
||||
{
|
||||
name: "Invalid InitContainer in Restore hook should return nil as pod, and error.",
|
||||
podInput: corev1api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "new",
|
||||
},
|
||||
Spec: corev1api.PodSpec{},
|
||||
},
|
||||
expectedError: fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field"),
|
||||
expectedPod: nil,
|
||||
restoreHooks: []ResourceRestoreHook{
|
||||
{
|
||||
Name: "hook1",
|
||||
Selector: ResourceHookSelector{
|
||||
Namespaces: collections.NewIncludesExcludes().Includes("new"),
|
||||
Resources: collections.NewIncludesExcludes().Includes(kuberesource.Pods.Resource),
|
||||
},
|
||||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init-container-1", "nginx").
|
||||
ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
@ -1931,10 +1944,32 @@ func TestHandleRestoreHooks(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
actual, err := handler.HandleRestoreHooks(velerotest.NewLogger(), kuberesource.Pods, &unstructured.Unstructured{Object: podMap}, tc.restoreHooks, tc.namespaceMapping)
|
||||
assert.Equal(t, tc.expectedError, err)
|
||||
actualPod := new(corev1api.Pod)
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(actual.UnstructuredContent(), actualPod)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedPod, actualPod)
|
||||
if actual != nil {
|
||||
actualPod := new(corev1api.Pod)
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(actual.UnstructuredContent(), actualPod)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedPod, actualPod)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateContainer(t *testing.T) {
|
||||
valid := `{"name": "test", "image": "busybox", "command": ["pwd"]}`
|
||||
noName := `{"image": "busybox", "command": ["pwd"]}`
|
||||
noImage := `{"name": "test", "command": ["pwd"]}`
|
||||
noCommand := `{"name": "test", "image": "busybox"}`
|
||||
expectedError := fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field")
|
||||
|
||||
// valid string should return nil as result.
|
||||
assert.Equal(t, nil, ValidateContainer([]byte(valid)))
|
||||
|
||||
// noName string should return expected error as result.
|
||||
assert.Equal(t, expectedError, ValidateContainer([]byte(noName)))
|
||||
|
||||
// noImage string should return expected error as result.
|
||||
assert.Equal(t, expectedError, ValidateContainer([]byte(noImage)))
|
||||
|
||||
// noCommand string should return expected error as result.
|
||||
assert.Equal(t, expectedError, ValidateContainer([]byte(noCommand)))
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ limitations under the License.
|
|||
package v1
|
||||
|
||||
import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RestoreSpec defines the specification for a Velero restore.
|
||||
|
@ -208,9 +208,10 @@ type ExecRestoreHook struct {
|
|||
// InitRestoreHook is a hook that adds an init container to a PodSpec to run commands before the
|
||||
// workload pod is able to start.
|
||||
type InitRestoreHook struct {
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
// InitContainers is list of init containers to be added to a pod during its restore.
|
||||
// +optional
|
||||
InitContainers []v1.Container `json:"initContainers"`
|
||||
InitContainers []runtime.RawExtension `json:"initContainers"`
|
||||
|
||||
// Timeout defines the maximum amount of time Velero should wait for the initContainers to complete.
|
||||
// +optional
|
||||
|
|
|
@ -764,7 +764,7 @@ func (in *InitRestoreHook) DeepCopyInto(out *InitRestoreHook) {
|
|||
*out = *in
|
||||
if in.InitContainers != nil {
|
||||
in, out := &in.InitContainers, &out.InitContainers
|
||||
*out = make([]corev1.Container, len(*in))
|
||||
*out = make([]runtime.RawExtension, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
|
|
|
@ -17,9 +17,11 @@ limitations under the License.
|
|||
package builder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
apimachineryRuntime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// ContainerBuilder builds Container objects
|
||||
|
@ -89,6 +91,17 @@ func (b *ContainerBuilder) Result() *corev1api.Container {
|
|||
return b.object
|
||||
}
|
||||
|
||||
// ResultRawExtension returns the Container as runtime.RawExtension.
|
||||
func (b *ContainerBuilder) ResultRawExtension() apimachineryRuntime.RawExtension {
|
||||
result, err := json.Marshal(b.object)
|
||||
if err != nil {
|
||||
return apimachineryRuntime.RawExtension{}
|
||||
}
|
||||
return apimachineryRuntime.RawExtension{
|
||||
Raw: result,
|
||||
}
|
||||
}
|
||||
|
||||
// Args sets the container's Args.
|
||||
func (b *ContainerBuilder) Args(args ...string) *ContainerBuilder {
|
||||
b.object.Args = append(b.object.Args, args...)
|
||||
|
|
|
@ -38,6 +38,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
hook "github.com/vmware-tanzu/velero/internal/hook"
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
|
@ -328,6 +329,22 @@ func (c *restoreController) validateAndComplete(restore *api.Restore, pluginMana
|
|||
return backupInfo{}
|
||||
}
|
||||
|
||||
// validate Restore Init Hook's InitContainers
|
||||
restoreHooks, err := hook.GetRestoreHooksFromSpec(&restore.Spec.Hooks)
|
||||
if err != nil {
|
||||
restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, err.Error())
|
||||
}
|
||||
for _, resource := range restoreHooks {
|
||||
for _, h := range resource.RestoreHooks {
|
||||
for _, container := range h.Init.InitContainers {
|
||||
err = hook.ValidateContainer(container.Raw)
|
||||
if err != nil {
|
||||
restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if ScheduleName is specified, fill in BackupName with the most recent successful backup from
|
||||
// the schedule
|
||||
if restore.Spec.ScheduleName != "" {
|
||||
|
|
|
@ -90,11 +90,11 @@ func TestInitContainerRestoreHookPodActionExecute(t *testing.T) {
|
|||
PostHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Init: &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
*builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"foobarbaz"}).Result(),
|
||||
*builder.ForContainer("restore-init2", "busy-box").
|
||||
Command([]string{"foobarbaz"}).Result(),
|
||||
InitContainers: []runtime.RawExtension{
|
||||
builder.ForContainer("restore-init1", "busy-box").
|
||||
Command([]string{"foobarbaz"}).ResultRawExtension(),
|
||||
builder.ForContainer("restore-init2", "busy-box").
|
||||
Command([]string{"foobarbaz"}).ResultRawExtension(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue