Skip to content

Commit 0972b2f

Browse files
Return user friendly error when package doesn't exist (#1322)
Signed-off-by: rcmadhankumar <[email protected]>
1 parent 1ad322b commit 0972b2f

File tree

5 files changed

+80
-31
lines changed

5 files changed

+80
-31
lines changed

pkg/apiserver/apiserver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func NewAPIServer(clientConfig *rest.Config, coreClient kubernetes.Interface, kc
116116
}
117117

118118
packageMetadatasStorage := packagerest.NewPackageMetadataCRDREST(kcClient, coreClient, opts.GlobalNamespace)
119-
packageStorage := packagerest.NewPackageCRDREST(kcClient, coreClient, opts.GlobalNamespace)
119+
packageStorage := packagerest.NewPackageCRDREST(kcClient, coreClient, opts.GlobalNamespace, opts.Logger)
120120

121121
pkgGroup := genericapiserver.NewDefaultAPIGroupInfo(datapackaging.GroupName, Scheme, metav1.ParameterCodec, Codecs)
122122
pkgv1alpha1Storage := map[string]apirest.Storage{}

pkg/apiserver/registry/datapackaging/package_crd_rest.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"time"
1010

11+
"github.com/go-logr/logr"
1112
"github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/apis/datapackaging"
1213
"github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/apis/datapackaging/validation"
1314
installclient "github.com/vmware-tanzu/carvel-kapp-controller/pkg/client/clientset/versioned"
@@ -31,15 +32,17 @@ type PackageCRDREST struct {
3132
crdClient installclient.Interface
3233
nsClient kubernetes.Interface
3334
globalNamespace string
35+
logger logr.Logger
3436
}
3537

3638
var (
3739
_ rest.StandardStorage = &PackageCRDREST{}
3840
_ rest.ShortNamesProvider = &PackageCRDREST{}
3941
)
4042

41-
func NewPackageCRDREST(crdClient installclient.Interface, nsClient kubernetes.Interface, globalNS string) *PackageCRDREST {
42-
return &PackageCRDREST{crdClient, nsClient, globalNS}
43+
// NewPackageCRDREST creates a new instance of the PackageCRDREST type
44+
func NewPackageCRDREST(crdClient installclient.Interface, nsClient kubernetes.Interface, globalNS string, logger logr.Logger) *PackageCRDREST {
45+
return &PackageCRDREST{crdClient, nsClient, globalNS, logger}
4346
}
4447

4548
func (r *PackageCRDREST) ShortNames() []string {
@@ -70,7 +73,7 @@ func (r *PackageCRDREST) NewList() runtime.Object {
7073

7174
func (r *PackageCRDREST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
7275
namespace := request.NamespaceValue(ctx)
73-
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace))
76+
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace, r.logger))
7477

7578
if createValidation != nil {
7679
if err := createValidation(ctx, obj); err != nil {
@@ -98,7 +101,7 @@ func (r *PackageCRDREST) shouldFetchGlobal(ctx context.Context, namespace string
98101

99102
func (r *PackageCRDREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
100103
namespace := request.NamespaceValue(ctx)
101-
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace))
104+
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace, r.logger))
102105

103106
pkg, err := client.Get(ctx, namespace, name, *options)
104107
if errors.IsNotFound(err) && r.shouldFetchGlobal(ctx, namespace) {
@@ -109,7 +112,7 @@ func (r *PackageCRDREST) Get(ctx context.Context, name string, options *metav1.G
109112

110113
func (r *PackageCRDREST) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
111114
namespace := request.NamespaceValue(ctx)
112-
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace))
115+
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace, r.logger))
113116

114117
// field selector isnt supported by CRD's so reset it, we will apply it later
115118
fs := options.FieldSelector
@@ -157,7 +160,7 @@ func (r *PackageCRDREST) List(ctx context.Context, options *internalversion.List
157160

158161
func (r *PackageCRDREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
159162
namespace := request.NamespaceValue(ctx)
160-
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace))
163+
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace, r.logger))
161164

162165
pkg, err := client.Get(ctx, namespace, name, metav1.GetOptions{})
163166
if errors.IsNotFound(err) {
@@ -225,7 +228,7 @@ func (r *PackageCRDREST) Update(ctx context.Context, name string, objInfo rest.U
225228

226229
func (r *PackageCRDREST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
227230
namespace := request.NamespaceValue(ctx)
228-
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace))
231+
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace, r.logger))
229232

230233
pkg, err := client.Get(ctx, namespace, name, metav1.GetOptions{})
231234
if errors.IsNotFound(err) {
@@ -252,7 +255,7 @@ func (r *PackageCRDREST) Delete(ctx context.Context, name string, deleteValidati
252255

253256
func (r *PackageCRDREST) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error) {
254257
namespace := request.NamespaceValue(ctx)
255-
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace))
258+
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace, r.logger))
256259

257260
// clear unsupported field selectors
258261
fs := listOptions.FieldSelector
@@ -300,7 +303,7 @@ func (r *PackageCRDREST) DeleteCollection(ctx context.Context, deleteValidation
300303

301304
func (r *PackageCRDREST) Watch(ctx context.Context, options *internalversion.ListOptions) (watch.Interface, error) {
302305
namespace := request.NamespaceValue(ctx)
303-
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace))
306+
client := NewPackageStorageClient(r.crdClient, NewPackageTranslator(namespace, r.logger))
304307

305308
watcher, err := client.Watch(ctx, namespace, r.internalToMetaListOpts(*options))
306309
if errors.IsNotFound(err) && namespace != r.globalNamespace {

pkg/apiserver/registry/datapackaging/package_crd_rest_test.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111
"testing"
1212

13+
"github.com/stretchr/testify/require"
1314
"github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/internalpackaging/v1alpha1"
1415
"github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/apis/datapackaging"
1516
datapkgreg "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/registry/datapackaging"
@@ -20,6 +21,7 @@ import (
2021
"k8s.io/apimachinery/pkg/runtime"
2122
k8sfake "k8s.io/client-go/kubernetes/fake"
2223
cgtesting "k8s.io/client-go/testing"
24+
logf "sigs.k8s.io/controller-runtime/pkg/log"
2325
)
2426

2527
const globalNamespace = "global.packaging.kapp-controller.carvel.dev"
@@ -31,7 +33,7 @@ func TestPackageVersionListIncludesGlobalAndNamespaced(t *testing.T) {
3133
internalClient := fake.NewSimpleClientset(globalIntPackageVersion(), namespacedIntPackageVersion(), excludedNonGlobalIntPackageVersion())
3234
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
3335

34-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
36+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
3537

3638
pkgvList, err := pkgvCRDREST.List(namespacedCtx(nonGlobalNamespace), &internalversion.ListOptions{})
3739
if err != nil {
@@ -66,7 +68,7 @@ func TestPackageVersionListPrefersNamespacedOverGlobal(t *testing.T) {
6668
internalClient := fake.NewSimpleClientset(globalIntPackageVersion(), overrideIntPackageVersion())
6769
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
6870

69-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
71+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
7072

7173
// list package versions and verify all of them are there
7274
pkgvList, err := pkgvCRDREST.List(namespacedCtx(nonGlobalNamespace), &internalversion.ListOptions{})
@@ -101,7 +103,7 @@ func TestPackageVersionGetNotPresentInNS(t *testing.T) {
101103
internalClient := fake.NewSimpleClientset(globalPackageVersion)
102104
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
103105

104-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
106+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
105107

106108
obj, err := pkgvCRDREST.Get(namespacedCtx(nonGlobalNamespace), name, &metav1.GetOptions{})
107109
if err != nil {
@@ -126,7 +128,7 @@ func TestPackageVersionGetPresentInOnlyNS(t *testing.T) {
126128
internalClient := fake.NewSimpleClientset(namespacedPackageVersion)
127129
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
128130

129-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
131+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
130132

131133
obj, err := pkgvCRDREST.Get(namespacedCtx(nonGlobalNamespace), name, &metav1.GetOptions{})
132134
if err != nil {
@@ -146,21 +148,20 @@ func TestPackageVersionGetPresentInOnlyNS(t *testing.T) {
146148
func TestPackageVersionGetNotFound(t *testing.T) {
147149
namespacedPackageVersion := excludedNonGlobalIntPackageVersion()
148150
name := namespacedPackageName
151+
expectedError := "package.data.packaging.carvel.dev \"" + name + "\" not found"
149152

150153
internalClient := fake.NewSimpleClientset(namespacedPackageVersion)
151154
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
152155

153-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
156+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
154157

155158
_, err := pkgvCRDREST.Get(namespacedCtx(nonGlobalNamespace), name, &metav1.GetOptions{})
156159
if err == nil {
157160
t.Fatalf("Expected get operation to fail, but it didn't")
158161
}
159162

160-
if !errors.IsNotFound(err) {
161-
t.Fatalf("Expected a not found error, got: %v", err)
162-
}
163-
163+
require.True(t, errors.IsNotFound(err))
164+
require.ErrorContains(t, err, expectedError)
164165
}
165166

166167
func TestPackageVersionGetPreferNS(t *testing.T) {
@@ -171,7 +172,7 @@ func TestPackageVersionGetPreferNS(t *testing.T) {
171172
internalClient := fake.NewSimpleClientset(overridePackageVersion, globalIntPackageVersion())
172173
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
173174

174-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
175+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
175176

176177
obj, err := pkgvCRDREST.Get(namespacedCtx(nonGlobalNamespace), name, &metav1.GetOptions{})
177178
if err != nil {
@@ -201,7 +202,7 @@ func TestPackageVersionUpdateDoesntUpdateGlobal(t *testing.T) {
201202
internalClient := fake.NewSimpleClientset(globalPackageVersion, namespacedPackageVersion)
202203
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
203204

204-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
205+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
205206

206207
obj, created, err := pkgvCRDREST.Update(namespacedCtx(nonGlobalNamespace), name, UpdatePackageVersionTestImpl{updateReleaseNotesFn(newReleaseNotes, name, packageName, version)}, nil, nil, false, &metav1.UpdateOptions{})
207208
if err != nil {
@@ -242,7 +243,7 @@ func TestPackageVersionUpdateCreatesInNS(t *testing.T) {
242243
internalClient := fake.NewSimpleClientset(globalPackageVersion)
243244
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
244245

245-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
246+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
246247

247248
obj, created, err := pkgvCRDREST.Update(namespacedCtx(nonGlobalNamespace), name, UpdatePackageVersionTestImpl{updateReleaseNotesFn(newReleaseNotes, name, packageName, version)}, nil, nil, false, &metav1.UpdateOptions{})
248249
if err != nil {
@@ -276,7 +277,7 @@ func TestPackageVersionDeleteExistsInNS(t *testing.T) {
276277
internalClient := fake.NewSimpleClientset(namespacedPackageVersion)
277278
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
278279

279-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
280+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
280281

281282
_, _, err := pkgvCRDREST.Delete(namespacedCtx(nonGlobalNamespace), name, nil, &metav1.DeleteOptions{})
282283
if err != nil {
@@ -308,7 +309,7 @@ func TestPackageVersionDeleteExistsGlobalNotInNS(t *testing.T) {
308309
internalClient := fake.NewSimpleClientset(globalPackageVersion)
309310
fakeCoreClient := k8sfake.NewSimpleClientset(namespace())
310311

311-
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace)
312+
pkgvCRDREST := datapkgreg.NewPackageCRDREST(internalClient, fakeCoreClient, globalNamespace, logf.Log)
312313

313314
_, _, err := pkgvCRDREST.Delete(namespacedCtx(nonGlobalNamespace), name, nil, &metav1.DeleteOptions{})
314315
if !errors.IsNotFound(err) {

pkg/apiserver/registry/datapackaging/package_storage_client.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ package datapackaging
66
import (
77
"context"
88
"encoding/base32"
9+
"errors"
910
"fmt"
1011
"strings"
1112

13+
"github.com/go-logr/logr"
1214
internalpkgingv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/internalpackaging/v1alpha1"
1315
"github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/apis/datapackaging"
1416
datapkgingv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/apis/datapackaging/v1alpha1"
1517
"github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/watchers"
1618
internalclient "github.com/vmware-tanzu/carvel-kapp-controller/pkg/client/clientset/versioned"
17-
"k8s.io/apimachinery/pkg/api/errors"
19+
apierrors "k8s.io/apimachinery/pkg/api/errors"
1820
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1921
"k8s.io/apimachinery/pkg/fields"
2022
"k8s.io/apimachinery/pkg/watch"
@@ -27,10 +29,12 @@ const (
2729

2830
type PackageTranslator struct {
2931
namespace string
32+
logger logr.Logger
3033
}
3134

32-
func NewPackageTranslator(namespace string) PackageTranslator {
33-
return PackageTranslator{namespace}
35+
// NewPackageTranslator creates a new instance of the PackageTranslator type
36+
func NewPackageTranslator(namespace string, logger logr.Logger) PackageTranslator {
37+
return PackageTranslator{namespace, logger}
3438
}
3539

3640
func (t PackageTranslator) ToInternalName(name string) string {
@@ -65,7 +69,7 @@ func (t PackageTranslator) ToExternalObj(intObj *internalpkgingv1alpha1.Internal
6569
var err error
6670
obj.Name, err = t.ToExternalName(intObj.Name)
6771
if err != nil {
68-
return nil, errors.NewInternalError(fmt.Errorf("decoding internal obj name '%s': %v", intObj.Name, err))
72+
return nil, apierrors.NewInternalError(fmt.Errorf("decoding internal obj name '%s': %v", intObj.Name, err))
6973
}
7074

7175
// Self link is deprecated and planned for removal, so we don't translate it
@@ -122,10 +126,10 @@ func (t PackageTranslator) ToExternalWatcher(intObjWatcher watch.Interface, fiel
122126
evt.Object, err = t.ToExternalObj(intpkg)
123127
if err != nil {
124128
var status metav1.Status
125-
if statusErr, ok := err.(*errors.StatusError); ok {
129+
if statusErr, ok := err.(*apierrors.StatusError); ok {
126130
status = statusErr.Status()
127131
} else {
128-
status = errors.NewInternalError(err).Status()
132+
status = apierrors.NewInternalError(err).Status()
129133
}
130134
return watch.Event{Type: watch.Error, Object: &status}
131135
}
@@ -152,7 +156,29 @@ func (t PackageTranslator) ToExternalWatcher(intObjWatcher watch.Interface, fiel
152156
}
153157

154158
func (t PackageTranslator) ToExternalError(err error) error {
155-
// TODO: implement
159+
var status apierrors.APIStatus
160+
if !(errors.As(err, &status)) {
161+
return err
162+
}
163+
164+
details := status.Status().Details
165+
if details != nil && details.Kind == "internalpackages" && details.Group == internalpkgingv1alpha1.SchemeGroupVersion.Group {
166+
packageName, translateErr := t.ToExternalName(details.Name)
167+
if translateErr != nil {
168+
t.logger.Error(translateErr, "Error translating to external name")
169+
return err
170+
}
171+
172+
switch status.Status().Reason {
173+
case metav1.StatusReasonNotFound:
174+
return apierrors.NewNotFound(datapkgingv1alpha1.Resource("package"), packageName)
175+
case metav1.StatusReasonAlreadyExists:
176+
return apierrors.NewAlreadyExists(datapkgingv1alpha1.Resource("package"), packageName)
177+
default:
178+
return err
179+
}
180+
}
181+
156182
return err
157183
}
158184

test/e2e/kappcontroller/package_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"testing"
1111
"time"
1212

13+
"github.com/stretchr/testify/require"
1314
"github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/apis/datapackaging/v1alpha1"
1415
"github.com/vmware-tanzu/carvel-kapp-controller/test/e2e"
1516
"sigs.k8s.io/yaml"
@@ -409,3 +410,21 @@ spec:
409410
}
410411
})
411412
}
413+
414+
func TestPackageNotFound(t *testing.T) {
415+
env := e2e.BuildEnv(t)
416+
logger := e2e.Logger{}
417+
k := e2e.Kubectl{t, env.Namespace, logger}
418+
packageName := "foo.1.0.0"
419+
expectedError := "stderr: 'Error from server (NotFound): package.data.packaging.carvel.dev \"foo.1.0.0\" not found"
420+
421+
logger.Section("Get Package", func() {
422+
_, err := k.RunWithOpts([]string{"get", "package", packageName}, e2e.RunOpts{AllowError: true})
423+
require.ErrorContains(t, err, expectedError)
424+
})
425+
426+
logger.Section("delete Package", func() {
427+
_, err := k.RunWithOpts([]string{"delete", "package", packageName}, e2e.RunOpts{AllowError: true})
428+
require.ErrorContains(t, err, expectedError)
429+
})
430+
}

0 commit comments

Comments
 (0)