Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ingressclass from ingress #7813

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelogs/unreleased/7813-AlbeeSo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ingressClass resource to backup or restore if ingress is backuped and its spec.ingressClass is not nil.
69 changes: 69 additions & 0 deletions pkg/backup/actions/ingress_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
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 actions

import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
networkapi "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/runtime"

v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/kuberesource"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
)

// IngressAction implements ItemAction.
type IngressAction struct {
log logrus.FieldLogger
}

// NewIngressAction creates a new ItemAction for pods.
func NewIngressAction(logger logrus.FieldLogger) *IngressAction {
return &IngressAction{log: logger}
}

// AppliesTo returns a ResourceSelector that applies only to pods.
func (a *IngressAction) AppliesTo() (velero.ResourceSelector, error) {
return velero.ResourceSelector{
IncludedResources: []string{"ingresses"},
}, nil
}

// Execute scans the ingress's spec.ingressClassName for ingressClass and returns a
// ResourceIdentifier list containing references to the ingressClass used by
// the ingress. This ensures that when a ingress is backed up, related ingressClass is backed up too.
func (a *IngressAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
a.log.Info("Executing ingressAction")
defer a.log.Info("Done executing ingressAction")

ing := new(networkapi.Ingress)
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), ing); err != nil {
return nil, nil, errors.WithStack(err)

Check warning on line 56 in pkg/backup/actions/ingress_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/backup/actions/ingress_action.go#L56

Added line #L56 was not covered by tests
}

var additionalItems []velero.ResourceIdentifier
if ing.Spec.IngressClassName != nil && *ing.Spec.IngressClassName != "" {
a.log.Infof("Adding IngressClass %s to additionalItems", *ing.Spec.IngressClassName)
additionalItems = append(additionalItems, velero.ResourceIdentifier{
GroupResource: kuberesource.IngressClasses,
Name: *ing.Spec.IngressClassName,
})
}

return item, additionalItems, nil
}
109 changes: 109 additions & 0 deletions pkg/backup/actions/ingress_action_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
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 actions

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime"

"github.com/vmware-tanzu/velero/pkg/kuberesource"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)

func TestIngressActionAppliesTo(t *testing.T) {
a := NewIngressAction(velerotest.NewLogger())

actual, err := a.AppliesTo()
require.NoError(t, err)

expected := velero.ResourceSelector{
IncludedResources: []string{"ingresses"},
}
assert.Equal(t, expected, actual)
}

func TestIngressActionExecute(t *testing.T) {
tests := []struct {
name string
ing runtime.Unstructured
expected []velero.ResourceIdentifier
}{
{
name: "no spec.ingressClass",
ing: velerotest.UnstructuredOrDie(`
{
"apiVersion": "networking.k8s.io/v1",
"kind": "Ingress",
"metadata": {
"namespace": "ns",
"name": "ing-1"
}
}
`),
},
{
name: "spec.ingressClass equals to nil string",
ing: velerotest.UnstructuredOrDie(`
{
"apiVersion": "networking.k8s.io/v1",
"kind": "Ingress",
"metadata": {
"namespace": "ns",
"name": "ing-2"
},
"spec": {
"ingressClassName": ""
}
}
`),
},
{
name: "spec.ingressClass exists",
ing: velerotest.UnstructuredOrDie(`
{
"apiVersion": "networking.k8s.io/v1",
"kind": "Ingress",
"metadata": {
"namespace": "ns",
"name": "ing-3"
},
"spec": {
"ingressClassName": "ingressclass"
}
}
`),
expected: []velero.ResourceIdentifier{
{GroupResource: kuberesource.IngressClasses, Name: "ingressclass"},
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
a := NewIngressAction(velerotest.NewLogger())

updated, additionalItems, err := a.Execute(test.ing, nil)
require.NoError(t, err)
assert.Equal(t, test.ing, updated)
assert.Equal(t, test.expected, additionalItems)
})
}
}
16 changes: 16 additions & 0 deletions pkg/cmd/server/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
"velero.io/pod",
newPodBackupItemAction,
).
RegisterBackupItemAction(
"velero.io/ingress",
newIngressBackupItemAction,
).

Check warning on line 58 in pkg/cmd/server/plugin/plugin.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/server/plugin/plugin.go#L55-L58

Added lines #L55 - L58 were not covered by tests
RegisterBackupItemAction(
"velero.io/service-account",
newServiceAccountBackupItemAction(f),
Expand Down Expand Up @@ -88,6 +92,10 @@
"velero.io/add-pv-from-pvc",
newAddPVFromPVCRestoreItemAction,
).
RegisterRestoreItemAction(
"velero.io/add-ingressclass-from-ing",
newAddIngressClassFromIngAction,
).

Check warning on line 98 in pkg/cmd/server/plugin/plugin.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/server/plugin/plugin.go#L95-L98

Added lines #L95 - L98 were not covered by tests
RegisterRestoreItemAction(
"velero.io/change-storage-class",
newChangeStorageClassRestoreItemAction(f),
Expand Down Expand Up @@ -196,6 +204,10 @@
return bia.NewPodAction(logger), nil
}

func newIngressBackupItemAction(logger logrus.FieldLogger) (interface{}, error) {
return bia.NewIngressAction(logger), nil

Check warning on line 208 in pkg/cmd/server/plugin/plugin.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/server/plugin/plugin.go#L207-L208

Added lines #L207 - L208 were not covered by tests
}

func newServiceAccountBackupItemAction(f client.Factory) plugincommon.HandlerInitializer {
return func(logger logrus.FieldLogger) (interface{}, error) {
// TODO(ncdc): consider a k8s style WantsKubernetesClientSet initialization approach
Expand Down Expand Up @@ -290,6 +302,10 @@
return ria.NewAddPVFromPVCAction(logger), nil
}

func newAddIngressClassFromIngAction(logger logrus.FieldLogger) (interface{}, error) {
return ria.NewAddIngressClassFromIngAction(logger), nil

Check warning on line 306 in pkg/cmd/server/plugin/plugin.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/server/plugin/plugin.go#L305-L306

Added lines #L305 - L306 were not covered by tests
}

func newCRDV1PreserveUnknownFieldsItemAction(logger logrus.FieldLogger) (interface{}, error) {
return ria.NewCRDV1PreserveUnknownFieldsAction(logger), nil
}
Expand Down
1 change: 1 addition & 0 deletions pkg/kuberesource/kuberesource.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ var (
VolumeSnapshots = schema.GroupResource{Group: "snapshot.storage.k8s.io", Resource: "volumesnapshots"}
VolumeSnapshotContents = schema.GroupResource{Group: "snapshot.storage.k8s.io", Resource: "volumesnapshotcontents"}
PriorityClasses = schema.GroupResource{Group: "scheduling.k8s.io", Resource: "priorityclasses"}
IngressClasses = schema.GroupResource{Group: "networking.k8s.io", Resource: "ingressclasses"}
DataUploads = schema.GroupResource{Group: "velero.io", Resource: "datauploads"}
)
64 changes: 64 additions & 0 deletions pkg/restore/actions/add_ingressclass_from_ing_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright 2019 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 actions

import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
networkapi "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/runtime"

"github.com/vmware-tanzu/velero/pkg/kuberesource"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
)

type AddIngressClassFromIngAction struct {
logger logrus.FieldLogger
}

func NewAddIngressClassFromIngAction(logger logrus.FieldLogger) *AddIngressClassFromIngAction {
return &AddIngressClassFromIngAction{logger: logger}

Check warning on line 34 in pkg/restore/actions/add_ingressclass_from_ing_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/actions/add_ingressclass_from_ing_action.go#L33-L34

Added lines #L33 - L34 were not covered by tests
}

func (a *AddIngressClassFromIngAction) AppliesTo() (velero.ResourceSelector, error) {
return velero.ResourceSelector{
IncludedResources: []string{"ingresses"},
}, nil

Check warning on line 40 in pkg/restore/actions/add_ingressclass_from_ing_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/actions/add_ingressclass_from_ing_action.go#L37-L40

Added lines #L37 - L40 were not covered by tests
}

func (a *AddIngressClassFromIngAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
a.logger.Info("Executing AddIngressClassFromIngAction")

var ing networkapi.Ingress
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(input.Item.UnstructuredContent(), &ing); err != nil {
return nil, errors.Wrap(err, "unable to convert unstructured item to ingress")

Check warning on line 48 in pkg/restore/actions/add_ingressclass_from_ing_action.go

View check run for this annotation

Codecov / codecov/patch

pkg/restore/actions/add_ingressclass_from_ing_action.go#L48

Added line #L48 was not covered by tests
}

var additionalItems []velero.ResourceIdentifier

if ing.Spec.IngressClassName != nil && *ing.Spec.IngressClassName != "" {
a.logger.Infof("Adding IngressClass %s as an additional item to restore", *ing.Spec.IngressClassName)
additionalItems = append(additionalItems, velero.ResourceIdentifier{
GroupResource: kuberesource.IngressClasses,
Name: *ing.Spec.IngressClassName,
})
}
return &velero.RestoreItemActionExecuteOutput{
UpdatedItem: input.Item,
AdditionalItems: additionalItems,
}, nil
}
80 changes: 80 additions & 0 deletions pkg/restore/actions/add_ingressclass_from_ing_action_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
Copyright 2019 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 actions

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
networkapi "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/pointer"

"github.com/vmware-tanzu/velero/pkg/kuberesource"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)

func TestAddIngressClassFromIngActionExecute(t *testing.T) {
tests := []struct {
name string
item *networkapi.Ingress
want []velero.ResourceIdentifier
}{
{
name: "ingress with no related ingressClass",
item: &networkapi.Ingress{},
want: nil,
},
{
name: "ingress with related ingressClass returns ingressClass as additional item",
item: &networkapi.Ingress{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns-1",
Name: "ing",
},
Spec: networkapi.IngressSpec{
IngressClassName: pointer.StringPtr("ingressclass"),
},
},
want: []velero.ResourceIdentifier{
{GroupResource: kuberesource.IngressClasses, Name: "ingressclass"},
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
itemData, err := runtime.DefaultUnstructuredConverter.ToUnstructured(test.item)
require.NoError(t, err)

action := &AddIngressClassFromIngAction{logger: velerotest.NewLogger()}

input := &velero.RestoreItemActionExecuteInput{
Item: &unstructured.Unstructured{Object: itemData},
}

res, err := action.Execute(input)
require.NoError(t, err)

assert.Equal(t, test.want, res.AdditionalItems)
})
}
}
Loading