Skip to content

Commit 66c2c22

Browse files
kamiiiela-cordier
authored andcommitted
refactor: move webhook validations to the internal package
1 parent f7e6318 commit 66c2c22

File tree

7 files changed

+293
-139
lines changed

7 files changed

+293
-139
lines changed

api/model/refs/namespace.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,46 @@ import (
2020
"k8s.io/apimachinery/pkg/types"
2121
)
2222

23-
var _ custom.ResourceRef = NamespacedName{}
23+
var _ custom.ResourceRef = &NamespacedName{}
2424

2525
type NamespacedName struct {
2626
Name string `json:"name"`
2727
Namespace string `json:"namespace,omitempty"`
2828
}
2929

30+
// SetNamespace implements custom.ResourceRef.
31+
func (n *NamespacedName) SetNamespace(ns string) {
32+
n.Namespace = ns
33+
}
34+
35+
// IsMissingNamespace implements custom.ResourceRef.
36+
func (n *NamespacedName) IsMissingNamespace() bool {
37+
return !n.HasNameSpace()
38+
}
39+
3040
// GetName implements custom.ResourceRef.
31-
func (n NamespacedName) GetName() string {
41+
func (n *NamespacedName) GetName() string {
3242
return n.Name
3343
}
3444

3545
// GetNamespace implements custom.ResourceRef.
36-
func (n NamespacedName) GetNamespace() string {
46+
func (n *NamespacedName) GetNamespace() string {
3747
return n.Namespace
3848
}
3949

50+
// HasNameSpace implements custom.ResourceRef.
51+
func (n *NamespacedName) HasNameSpace() bool {
52+
return n.Namespace != ""
53+
}
54+
4055
func NewNamespacedName(namespace, name string) NamespacedName {
4156
return NamespacedName{Namespace: namespace, Name: name}
4257
}
4358

44-
func (n NamespacedName) NamespacedName() types.NamespacedName {
59+
func (n *NamespacedName) NamespacedName() types.NamespacedName {
4560
return types.NamespacedName{Namespace: n.Namespace, Name: n.Name}
4661
}
4762

48-
func (n NamespacedName) String() string {
63+
func (n *NamespacedName) String() string {
4964
return n.Namespace + "/" + n.Name
5065
}

api/v1alpha1/apiv4definition_webhook.go

Lines changed: 3 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,9 @@ package v1alpha1
1818

1919
import (
2020
"context"
21-
"fmt"
22-
"net/url"
23-
"strings"
2421

25-
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/env"
26-
27-
"sigs.k8s.io/controller-runtime/pkg/client"
28-
29-
v4 "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/v4"
3022
commonMutate "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/common/mutate"
31-
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s"
23+
wk "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/webhook"
3224
runtime "k8s.io/apimachinery/pkg/runtime"
3325
ctrl "sigs.k8s.io/controller-runtime"
3426
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -49,108 +41,13 @@ func (api *ApiV4Definition) Default() {
4941
}
5042

5143
func (api *ApiV4Definition) ValidateCreate() (admission.Warnings, error) {
52-
return validateApi(api)
44+
return wk.ValidateApiV4(context.Background(), &api.Spec.Api, api.Name, api.Namespace, api.Spec.Context)
5345
}
5446

5547
func (api *ApiV4Definition) ValidateUpdate(_ runtime.Object) (admission.Warnings, error) {
56-
return validateApi(api)
48+
return wk.ValidateApiV4(context.Background(), &api.Spec.Api, api.Name, api.Namespace, api.Spec.Context)
5749
}
5850

5951
func (*ApiV4Definition) ValidateDelete() (admission.Warnings, error) {
6052
return admission.Warnings{}, nil
6153
}
62-
63-
func validateApi(api *ApiV4Definition) (admission.Warnings, error) {
64-
// make sure Management Context exist before creating the API Definition resource
65-
if api.HasContext() { //nolint:nestif // nested if is needed
66-
mCtx := new(ManagementContext)
67-
if err := k8s.GetClient().Get(context.Background(), api.ContextRef().NamespacedName(), mCtx); err != nil {
68-
return admission.Warnings{}, fmt.Errorf("can't create API [%s] because it is using "+
69-
"management context [%v] that doesn't exist in the cluster", api.Name, api.ContextRef().NamespacedName())
70-
}
71-
} else {
72-
// check for unique context path
73-
apis := new(ApiV4DefinitionList)
74-
listOpts := client.ListOptions{}
75-
if !env.Config.CheckApiContextPathConflictInCluster {
76-
listOpts = client.ListOptions{
77-
Namespace: api.Namespace,
78-
}
79-
}
80-
81-
if err := k8s.GetClient().List(context.Background(), apis, &listOpts); err != nil {
82-
return admission.Warnings{}, err
83-
}
84-
85-
existingListeners := make([]*v4.GenericListener, 0)
86-
for _, item := range apis.Items {
87-
if api.Name != item.Name || api.Namespace != item.Namespace {
88-
existingListeners = append(existingListeners, item.Spec.Listeners...)
89-
}
90-
}
91-
92-
if err := validateApiContextPath(existingListeners, api.Spec.Listeners); err != nil {
93-
return admission.Warnings{}, err
94-
}
95-
}
96-
97-
return admission.Warnings{}, nil
98-
}
99-
100-
func validateApiContextPath(existingListeners, listeners []*v4.GenericListener) error {
101-
apiPaths := make([]string, 0)
102-
for _, l := range listeners {
103-
for _, s := range parseListener(l) {
104-
p, err := url.Parse(s)
105-
if err != nil {
106-
return err
107-
}
108-
apiPaths = append(apiPaths, p.String())
109-
}
110-
}
111-
112-
for _, l := range existingListeners {
113-
paths := parseListener(l)
114-
err := findDuplicatePath(paths, apiPaths)
115-
if err != nil {
116-
return err
117-
}
118-
}
119-
120-
return nil
121-
}
122-
123-
func parseListener(l v4.Listener) []string {
124-
if l == nil {
125-
return []string{}
126-
}
127-
128-
switch t := l.(type) {
129-
case *v4.GenericListener:
130-
return parseListener(t.ToListener())
131-
case *v4.HttpListener:
132-
{
133-
paths := make([]string, 0)
134-
for _, path := range t.Paths {
135-
p := fmt.Sprintf("%s/%s", path.Host, path.Path)
136-
paths = append(paths, strings.ReplaceAll(p, "//", "/"))
137-
}
138-
return paths
139-
}
140-
case *v4.TCPListener:
141-
return t.Hosts
142-
}
143-
144-
return []string{}
145-
}
146-
147-
func findDuplicatePath(existingPaths []string, newPaths []string) error {
148-
for _, ep := range existingPaths {
149-
for _, np := range newPaths {
150-
if ep == np {
151-
return fmt.Errorf("invalid API context path [%s]. Another API with the same path already exists", ep)
152-
}
153-
}
154-
}
155-
return nil
156-
}

api/v1alpha1/application_types.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,14 @@ limitations under the License.
1717
package v1alpha1
1818

1919
import (
20+
"fmt"
21+
2022
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/application"
2123
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/refs"
24+
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/hash"
25+
"github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
2226
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
2328
)
2429

2530
// Application is the main resource handled by the Kubernetes Operator
@@ -63,3 +68,63 @@ func (app *Application) IsBeingDeleted() bool {
6368
func init() {
6469
SchemeBuilder.Register(&Application{}, &ApplicationList{})
6570
}
71+
72+
// GetSpec implements custom.Resource.
73+
func (app *Application) GetSpec() custom.Spec {
74+
return &app.Spec
75+
}
76+
77+
// GetStatus implements custom.Resource.
78+
func (app *Application) GetStatus() custom.Status {
79+
return &app.Status
80+
}
81+
82+
func (app *Application) ContextRef() custom.ResourceRef {
83+
return app.Spec.Context
84+
}
85+
86+
func (app *Application) HasContext() bool {
87+
return app.Spec.Context != nil
88+
}
89+
90+
func (app *Application) ID() string {
91+
return app.Status.ID
92+
}
93+
94+
func (app *Application) DeepCopyResource() custom.Resource {
95+
return app.DeepCopy()
96+
}
97+
98+
func (spec *ApplicationSpec) Hash() string {
99+
return hash.Calculate(spec)
100+
}
101+
102+
func (s *ApplicationStatus) DeepCopyFrom(obj client.Object) error {
103+
switch t := obj.(type) {
104+
case *Application:
105+
t.Status.DeepCopyInto(s)
106+
default:
107+
return fmt.Errorf("unknown type %T", t)
108+
}
109+
110+
return nil
111+
}
112+
113+
func (s *ApplicationStatus) DeepCopyTo(api client.Object) error {
114+
switch t := api.(type) {
115+
case *Application:
116+
s.DeepCopyInto(&t.Status)
117+
default:
118+
return fmt.Errorf("unknown type %T", t)
119+
}
120+
121+
return nil
122+
}
123+
124+
func (s *ApplicationStatus) SetObservedGeneration(g int64) {
125+
s.ObservedGeneration = g
126+
}
127+
128+
func (s *ApplicationStatus) SetProcessingStatus(status custom.ProcessingStatus) {
129+
s.Status.ProcessingStatus = status
130+
}

api/v1alpha1/managementcontext_webhook.go

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,10 @@ package v1alpha1
1919
import (
2020
"context"
2121
"fmt"
22-
"net"
2322

24-
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim/client"
25-
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/http"
23+
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/refs"
24+
wk "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/webhook"
2625
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s"
27-
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/uuid"
28-
"github.com/pkg/errors"
2926
corev1 "k8s.io/api/core/v1"
3027
runtime "k8s.io/apimachinery/pkg/runtime"
3128
ctrl "sigs.k8s.io/controller-runtime"
@@ -67,29 +64,10 @@ func validateManagementContext(ctx *ManagementContext) (admission.Warnings, erro
6764
}
6865
}
6966

70-
if err := checkAPIAvailability(ctx); err != nil {
67+
ctxRef := refs.NewNamespacedName(ctx.Namespace, ctx.Name)
68+
if err := wk.CheckAPIMAvailability(&ctxRef); err != nil {
7169
return admission.Warnings{err.Error()}, nil //nolint:nilerr // changed to warning
7270
}
7371

7472
return admission.Warnings{}, nil
7573
}
76-
77-
func checkAPIAvailability(ctx *ManagementContext) error {
78-
urLs, _ := client.NewURLs(ctx.Spec.BaseUrl, ctx.Spec.OrgId, ctx.Spec.EnvId)
79-
80-
httpClient := http.NewClient(context.Background(), nil)
81-
cli := client.Client{
82-
HTTP: httpClient,
83-
URLs: urLs,
84-
}
85-
86-
api := make(map[string]interface{})
87-
err := httpClient.Get(cli.EnvV1Target("apis").WithPath(uuid.NewV4String()).String(), api)
88-
89-
var opError *net.OpError
90-
if errors.As(err, &opError) {
91-
return fmt.Errorf("unable to reach APIM, [%s] is not available", ctx.Spec.BaseUrl)
92-
}
93-
94-
return nil
95-
}

0 commit comments

Comments
 (0)