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
Xun Jiang 2022-07-01 07:18:58 +00:00
parent 108c81d84c
commit e8da5df57a
13 changed files with 207 additions and 1543 deletions

View File

@ -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

View File

@ -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"] }
]

View File

@ -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"] }
]

View File

@ -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

View File

@ -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
}

View File

@ -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)))
}

View File

@ -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

View File

@ -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])
}

View File

@ -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...)

View File

@ -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 != "" {

View File

@ -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(),
},
},
},