diff --git a/.golangci.yml b/.golangci.yml
index 9c6eab961..e8495d538 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -253,7 +253,7 @@ issues:
max-same-issues: 50
exclude:
- - ".*(Id|Api|Url|Http).* should be .*(ID|API|URL|HTTP).*"
+ - ".*(Api|Url|Http).* should be .*(API|URL|HTTP).*"
exclude-rules:
- source: "^//\\s*go:generate\\s"
diff --git a/api/model/api/base/member.go b/api/model/api/base/member.go
index f69caeeca..45e1d843f 100644
--- a/api/model/api/base/member.go
+++ b/api/model/api/base/member.go
@@ -22,7 +22,7 @@ type Member struct {
// Member source ID
// +kubebuilder:validation:Required
// +kubebuilder:example:=user@email.com
- SourceId string `json:"sourceId"`
+ SourceID string `json:"sourceId"`
// Member display name
DisplayName string `json:"displayName,omitempty"`
// The API role associated with this Member
@@ -33,7 +33,7 @@ type Member struct {
func NewGraviteeMember(username, role string) *Member {
return &Member{
Source: "gravitee",
- SourceId: username,
+ SourceID: username,
Role: role,
}
}
@@ -41,7 +41,7 @@ func NewGraviteeMember(username, role string) *Member {
func NewMemoryMember(username, role string) *Member {
return &Member{
Source: "memory",
- SourceId: username,
+ SourceID: username,
Role: role,
}
}
diff --git a/api/model/api/base/page.go b/api/model/api/base/page.go
index 75ab13787..96228128b 100644
--- a/api/model/api/base/page.go
+++ b/api/model/api/base/page.go
@@ -26,7 +26,7 @@ type PageSource struct {
type AccessControl struct {
// +kubebuilder:validation:Required
// The ID denied or granted by the access control (currently only group names are supported)
- ReferenceId string `json:"referenceId,omitempty"`
+ ReferenceID string `json:"referenceId,omitempty"`
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=GROUP;
// The type of reference denied or granted by the access control
diff --git a/api/model/api/base/plan.go b/api/model/api/base/plan.go
index f5b49f2cf..3182da149 100644
--- a/api/model/api/base/plan.go
+++ b/api/model/api/base/plan.go
@@ -30,11 +30,11 @@ type PlanValidation string
type Plan struct {
// Plan ID
- Id string `json:"id,omitempty"`
+ ID string `json:"id,omitempty"`
// The plan Cross ID.
// This field is used to identify plans defined for an API
// that has been promoted between different environments.
- CrossId string `json:"crossId,omitempty"`
+ CrossID string `json:"crossId,omitempty"`
// Plan Description
Description string `json:"description"`
// List of plan tags
@@ -71,11 +71,11 @@ func (plan *Plan) WithStatus(status PlanStatus) *Plan {
}
func (plan *Plan) WithID(id string) *Plan {
- plan.Id = id
+ plan.ID = id
return plan
}
func (plan *Plan) WithCrossID(id string) *Plan {
- plan.CrossId = id
+ plan.CrossID = id
return plan
}
diff --git a/api/model/api/base/primary_owner.go b/api/model/api/base/primary_owner.go
index 1b616fe0f..7593ace7d 100644
--- a/api/model/api/base/primary_owner.go
+++ b/api/model/api/base/primary_owner.go
@@ -17,7 +17,7 @@ package base
type PrimaryOwner struct {
// +kubebuilder:validation:Required
// PrimaryOwner ID
- Id string `json:"id,omitempty"`
+ ID string `json:"id,omitempty"`
// +kubebuilder:validation:Optional
// PrimaryOwner email
Email string `json:"email,omitempty"`
diff --git a/api/model/api/v2/api.go b/api/model/api/v2/api.go
index 653945e5e..ea601c8e3 100644
--- a/api/model/api/v2/api.go
+++ b/api/model/api/v2/api.go
@@ -17,8 +17,11 @@ package v2
import (
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/base"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
)
+var _ custom.ApiDefinition = &Api{}
+
type Api struct {
*base.ApiBase `json:",inline"`
// +kubebuilder:validation:Required
@@ -69,6 +72,15 @@ type Api struct {
ExecutionMode string `json:"execution_mode,omitempty"`
}
+func (api *Api) GetDefinitionVersion() custom.ApiDefinitionVersion {
+ return custom.ApiV2
+}
+
+// TODO implement when v2 admission handles paths
+func (api *Api) GetContextPaths() ([]string, error) {
+ return make([]string, 0), nil
+}
+
const (
ModeFullyManaged = "fully_managed"
OriginKubernetes = "kubernetes"
diff --git a/api/model/api/v2/plan.go b/api/model/api/v2/plan.go
index ee583f69f..933f2d3f7 100644
--- a/api/model/api/v2/plan.go
+++ b/api/model/api/v2/plan.go
@@ -28,7 +28,7 @@ type Consumer struct {
// Consumer type (possible values TAG)
ConsumerType ConsumerType `json:"consumerType,omitempty"`
// Consumer ID
- ConsumerId string `json:"consumerId,omitempty"`
+ ConsumerID string `json:"consumerId,omitempty"`
}
type Plan struct {
diff --git a/api/model/api/v4/api.go b/api/model/api/v4/api.go
index d50e6cb62..3e7b55d78 100644
--- a/api/model/api/v4/api.go
+++ b/api/model/api/v4/api.go
@@ -16,7 +16,10 @@
package v4
import (
+ "fmt"
+
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/base"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
)
// +kubebuilder:validation:Enum=PROXY;MESSAGE;
@@ -25,6 +28,8 @@ type ApiType string
// +kubebuilder:validation:Enum=PUBLISHED;UNPUBLISHED;
type ApiV4LifecycleState string
+var _ custom.ApiDefinition = &Api{}
+
type Api struct {
*base.ApiBase `json:",inline"`
// +kubebuilder:default:=`V4`
@@ -185,3 +190,43 @@ func (api *Api) getGatewayDefinitionEndpointGroups() []*EndpointGroup {
}
return endpointGroups
}
+
+func (api *Api) GetDefinitionVersion() custom.ApiDefinitionVersion {
+ return custom.ApiV4
+}
+
+func (api *Api) GetContextPaths() ([]string, error) {
+ paths := make([]string, 0)
+ for _, l := range api.Listeners {
+ paths = append(paths, parseListener(l)...)
+ }
+ return paths, nil
+}
+
+func parseListener(l Listener) []string {
+ if l == nil {
+ return []string{}
+ }
+
+ switch t := l.(type) {
+ case *GenericListener:
+ return parseListener(t.ToListener())
+ case *HttpListener:
+ {
+ paths := make([]string, 0)
+ for _, path := range t.Paths {
+ if path.Host != "" {
+ p := fmt.Sprintf("%s/%s", path.Host, path.Path)
+ paths = append(paths, p)
+ } else {
+ paths = append(paths, path.Path)
+ }
+ }
+ return paths
+ }
+ case *TCPListener:
+ return t.Hosts
+ }
+
+ return []string{}
+}
diff --git a/api/model/api/v4/status.go b/api/model/api/v4/status.go
index 6cc164526..6fb1defbb 100644
--- a/api/model/api/v4/status.go
+++ b/api/model/api/v4/status.go
@@ -14,7 +14,9 @@
package v4
-import "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/base"
+import (
+ "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/base"
+)
type Status struct {
base.Status `json:",inline"`
@@ -22,4 +24,18 @@ type Status struct {
// for the API definition if a management context has been defined
// to sync the API with an APIM instance
Plans map[string]string `json:"plans,omitempty"`
+ // When API has been created regardless of errors, this field is
+ // used to persist the error message encountered during admission
+ Errors Errors `json:"errors,omitempty"`
+}
+
+type Errors struct {
+ // warning errors do not block object reconciliation,
+ // most of the time because the value is ignored or defaulted
+ // when the API gets synced with APIM
+ Warning []string `json:"warning,omitempty"`
+ // severe errors do not pass admission and will block reconcile
+ // hence, this field should always be during the admission phase
+ // and is very unlikely to be persisted in the status
+ Severe []string `json:"severe,omitempty"`
}
diff --git a/api/model/api/v4/zz_generated.deepcopy.go b/api/model/api/v4/zz_generated.deepcopy.go
index a0639bea3..df800d3d4 100644
--- a/api/model/api/v4/zz_generated.deepcopy.go
+++ b/api/model/api/v4/zz_generated.deepcopy.go
@@ -432,6 +432,31 @@ func (in *Entrypoint) DeepCopy() *Entrypoint {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Errors) DeepCopyInto(out *Errors) {
+ *out = *in
+ if in.Warning != nil {
+ in, out := &in.Warning, &out.Warning
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ if in.Severe != nil {
+ in, out := &in.Severe, &out.Severe
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Errors.
+func (in *Errors) DeepCopy() *Errors {
+ if in == nil {
+ return nil
+ }
+ out := new(Errors)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Flow) DeepCopyInto(out *Flow) {
*out = *in
@@ -889,6 +914,7 @@ func (in *Status) DeepCopyInto(out *Status) {
(*out)[key] = val
}
}
+ in.Errors.DeepCopyInto(&out.Errors)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Status.
diff --git a/api/model/application/application.go b/api/model/application/application.go
index 5ce507a47..93711f3a8 100644
--- a/api/model/application/application.go
+++ b/api/model/application/application.go
@@ -21,15 +21,15 @@ type AppKeyMode string
type SimpleSettings struct {
// Application Type
AppType string `json:"type"`
- // ClientId is the client id of the application
- ClientId string `json:"client_id,omitempty"`
+ // ClientID is the client id of the application
+ ClientID string `json:"client_id,omitempty"`
}
type OAuthClientSettings struct {
// Oauth client application type
ApplicationType string `json:"application_type"`
// Oauth client id
- ClientId string `json:"client_id,omitempty"`
+ ClientID string `json:"client_id,omitempty"`
// Oauth client secret
ClientSecret string `json:"client_secret,omitempty"`
// Oauth client uri
@@ -77,8 +77,8 @@ type Application struct {
Description string `json:"description,omitempty"`
// Application Type
Type string `json:"type,omitempty"`
- // The ClientId identifying the application. This field is required when subscribing to an OAUTH2 / JWT plan.
- ClientId string `json:"clientId,omitempty"`
+ // The ClientID identifying the application. This field is required when subscribing to an OAUTH2 / JWT plan.
+ ClientID string `json:"clientId,omitempty"`
// List of application Redirect Uris
RedirectUris []string `json:"redirectUris,omitempty"`
diff --git a/api/model/management/context.go b/api/model/management/context.go
index e3ca30e5d..e8890d6c8 100644
--- a/api/model/management/context.go
+++ b/api/model/management/context.go
@@ -17,18 +17,23 @@ package management
import (
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/refs"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
)
+var _ custom.Auth = &Auth{}
+var _ custom.BasicAuth = &BasicAuth{}
+var _ custom.Context = &Context{}
+
type Context struct {
// The URL of a management API instance
// +kubebuilder:validation:Pattern=`^http(s?):\/\/.+$`
BaseUrl string `json:"baseUrl"`
// An existing organization id targeted by the context on the management API instance.
// +kubebuilder:validation:Required
- OrgId string `json:"organizationId"`
+ OrgID string `json:"organizationId"`
// An existing environment id targeted by the context within the organization.
// +kubebuilder:validation:Required
- EnvId string `json:"environmentId"`
+ EnvID string `json:"environmentId"`
// Auth defines the authentication method used to connect to the API Management.
// Can be either basic authentication credentials, a bearer token
// or a reference to a kubernetes secret holding one of these two configurations.
@@ -36,6 +41,31 @@ type Context struct {
Auth *Auth `json:"auth"`
}
+// GetAuth implements custom.Context.
+func (c *Context) GetAuth() custom.Auth {
+ return c.Auth
+}
+
+// GetEnvID implements custom.Context.
+func (c *Context) GetEnvID() string {
+ return c.EnvID
+}
+
+// GetOrgID implements custom.Context.
+func (c *Context) GetOrgID() string {
+ return c.OrgID
+}
+
+// GetSecretRef implements custom.Context.
+func (c *Context) GetSecretRef() custom.ResourceRef {
+ return c.Auth.SecretRef
+}
+
+// GetURL implements custom.Context.
+func (c *Context) GetURL() string {
+ return c.BaseUrl
+}
+
type Auth struct {
// The bearer token used to authenticate against the API Management instance
// (must be generated from an admin account)
@@ -46,6 +76,39 @@ type Auth struct {
SecretRef *refs.NamespacedName `json:"secretRef,omitempty"`
}
+// GetBearerToken implements custom.Auth.
+func (in *Auth) GetBearerToken() string {
+ return in.BearerToken
+}
+
+// HasCredentials implements custom.Auth.
+func (in *Auth) HasCredentials() bool {
+ return in.Credentials != nil
+}
+
+// GetCredentials implements custom.Auth.
+func (in *Auth) GetCredentials() custom.BasicAuth {
+ return in.Credentials
+}
+
+// GetSecretRef implements custom.Auth.
+func (in *Auth) GetSecretRef() custom.ResourceRef {
+ return in.SecretRef
+}
+
+// SetCredentials implements custom.Auth.
+func (in *Auth) SetCredentials(username string, password string) {
+ in.Credentials = &BasicAuth{
+ Username: username,
+ Password: password,
+ }
+}
+
+// SetToken implements custom.Auth.
+func (in *Auth) SetToken(token string) {
+ in.BearerToken = token
+}
+
type BasicAuth struct {
// +kubebuilder:validation:Required
Username string `json:"username,omitempty"`
@@ -53,6 +116,16 @@ type BasicAuth struct {
Password string `json:"password,omitempty"`
}
+// GetPassword implements custom.BasicAuth.
+func (in *BasicAuth) GetPassword() string {
+ return in.Password
+}
+
+// GetUsername implements custom.BasicAuth.
+func (in *BasicAuth) GetUsername() string {
+ return in.Username
+}
+
func (c *Context) HasAuthentication() bool {
return c.Auth != nil
}
diff --git a/api/v1alpha1/apiv2definition_types.go b/api/v1alpha1/apiv2definition_types.go
index a3644de9c..7de2cf61f 100644
--- a/api/v1alpha1/apiv2definition_types.go
+++ b/api/v1alpha1/apiv2definition_types.go
@@ -29,7 +29,6 @@ import (
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/refs"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/uuid"
"github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
- "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/list"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -61,8 +60,7 @@ type ApiDefinitionStatus struct {
base.Status `json:",inline"`
}
-var _ list.Item = &ApiDefinition{}
-var _ custom.ApiDefinition = &ApiDefinition{}
+var _ custom.ApiDefinitionResource = &ApiDefinition{}
var _ custom.Status = &ApiDefinitionStatus{}
var _ custom.Spec = &ApiDefinitionV2Spec{}
@@ -83,39 +81,6 @@ type ApiDefinition struct {
Status ApiDefinitionStatus `json:"status,omitempty"`
}
-// PickID returns the ID of the API definition, when a context has been defined at the spec level.
-// The ID might be returned from the API status, meaning that the API is already known.
-// If the API is unknown, the ID is either given from the spec if given,
-// or generated from the API UID and the context key to ensure uniqueness
-// in case the API is replicated on a same APIM instance.
-func (api *ApiDefinition) PickID() string {
- if api.Status.ID != "" {
- return api.Status.ID
- }
-
- if api.Spec.ID != "" {
- return api.Spec.ID
- }
-
- return string(api.UID)
-}
-
-func (api *ApiDefinition) PickCrossID() string {
- if api.Status.CrossID != "" {
- return api.Status.CrossID
- }
-
- return api.GetOrGenerateCrossID()
-}
-
-func (api *ApiDefinition) GetOrGenerateCrossID() string {
- if api.Spec.CrossID != "" {
- return api.Spec.CrossID
- }
-
- return uuid.FromStrings(api.GetNamespacedName().String())
-}
-
func (api *ApiDefinition) GetNamespacedName() *refs.NamespacedName {
return &refs.NamespacedName{Namespace: api.Namespace, Name: api.Name}
}
@@ -131,19 +96,19 @@ func (spec *ApiDefinitionV2Spec) SetDefinitionContext() {
}
}
-func (api *ApiDefinition) EnvID() string {
+func (api *ApiDefinition) GetEnvID() string {
return api.Status.EnvID
}
-func (api *ApiDefinition) ID() string {
+func (api *ApiDefinition) GetID() string {
return api.Status.ID
}
-func (api *ApiDefinition) OrgID() string {
+func (api *ApiDefinition) GetOrgID() string {
return api.Status.OrgID
}
-func (api *ApiDefinition) Version() custom.ApiDefinitionVersion {
+func (api *ApiDefinition) GetDefinitionVersion() custom.ApiDefinitionVersion {
return custom.ApiV2
}
@@ -167,6 +132,83 @@ func (api *ApiDefinition) HasContext() bool {
return api.Spec.Context != nil
}
+func (api *ApiDefinition) GetContextPaths() ([]string, error) {
+ return api.Spec.GetContextPaths()
+}
+
+func (api *ApiDefinition) GetDefinition() custom.ApiDefinition {
+ return &api.Spec.Api
+}
+
+func (api *ApiDefinition) PopulateIDs(_ custom.Context) {
+ api.Spec.ID = api.pickID()
+ api.Spec.CrossID = api.pickCrossID()
+ api.generateEmptyPlanCrossIDs()
+ api.generatePageIDs()
+}
+
+// For each plan, generate a Cross id from Api id & Plan Name if not defined.
+func (api *ApiDefinition) generateEmptyPlanCrossIDs() {
+ plans := api.Spec.Plans
+
+ for _, plan := range plans {
+ if plan.CrossID == "" {
+ plan.CrossID = uuid.FromStrings(api.Spec.ID, separator, plan.Name)
+ }
+ }
+}
+
+func (api *ApiDefinition) generatePageIDs() {
+ spec := &api.Spec
+ pages := spec.Pages
+ for name, page := range pages {
+ page.API = spec.ID
+ apiName := api.GetNamespacedName().String()
+ if page.CrossID == "" {
+ page.CrossID = uuid.FromStrings(apiName, separator, name)
+ }
+ if page.ID == "" {
+ page.ID = uuid.FromStrings(spec.ID, separator, name)
+ }
+ if page.Parent != "" {
+ page.ParentID = uuid.FromStrings(spec.ID, separator, page.Parent)
+ }
+ }
+}
+
+// PickID returns the ID of the API definition, when a context has been defined at the spec level.
+// The ID might be returned from the API status, meaning that the API is already known.
+// If the API is unknown, the ID is either given from the spec if given,
+// or generated from the API UID and the context key to ensure uniqueness
+// in case the API is replicated on a same APIM instance.
+func (api *ApiDefinition) pickID() string {
+ if api.Status.ID != "" {
+ return api.Status.ID
+ }
+
+ if api.Spec.ID != "" {
+ return api.Spec.ID
+ }
+
+ return string(api.UID)
+}
+
+func (api *ApiDefinition) pickCrossID() string {
+ if api.Status.CrossID != "" {
+ return api.Status.CrossID
+ }
+
+ return api.getOrGenerateCrossID()
+}
+
+func (api *ApiDefinition) getOrGenerateCrossID() string {
+ if api.Spec.CrossID != "" {
+ return api.Spec.CrossID
+ }
+
+ return uuid.FromStrings(api.GetNamespacedName().String())
+}
+
func (spec *ApiDefinitionV2Spec) Hash() string {
return hash.Calculate(spec)
}
@@ -201,8 +243,6 @@ func (s *ApiDefinitionStatus) DeepCopyTo(obj client.Object) error {
return nil
}
-var _ list.Interface = &ApiDefinitionList{}
-
// ApiDefinitionList contains a list of ApiDefinition.
// +kubebuilder:object:root=true
type ApiDefinitionList struct {
@@ -211,14 +251,6 @@ type ApiDefinitionList struct {
Items []ApiDefinition `json:"items"`
}
-func (l *ApiDefinitionList) GetItems() []list.Item {
- items := make([]list.Item, len(l.Items))
- for i := range l.Items {
- items[i] = &l.Items[i]
- }
- return items
-}
-
func init() {
SchemeBuilder.Register(&ApiDefinition{}, &ApiDefinitionList{})
}
diff --git a/api/v1alpha1/apiv2definition_webhook.go b/api/v1alpha1/apiv2definition_webhook.go
deleted file mode 100644
index 8d9db5824..000000000
--- a/api/v1alpha1/apiv2definition_webhook.go
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
- *
- * 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 v1alpha1
-
-import (
- commonMutate "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/common/mutate"
- runtime "k8s.io/apimachinery/pkg/runtime"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/webhook"
- "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
-)
-
-var _ webhook.Defaulter = &ApiDefinition{}
-var _ webhook.Validator = &ApiDefinition{}
-
-func (api *ApiDefinition) SetupWebhookWithManager(mgr ctrl.Manager) error {
- return ctrl.NewWebhookManagedBy(mgr).
- For(api).
- Complete()
-}
-
-func (api *ApiDefinition) Default() {
- commonMutate.SetDefaults(api)
-}
-
-func (api *ApiDefinition) ValidateCreate() (admission.Warnings, error) {
- return admission.Warnings{}, nil
-}
-
-func (api *ApiDefinition) ValidateUpdate(_ runtime.Object) (admission.Warnings, error) {
- return admission.Warnings{}, nil
-}
-
-func (*ApiDefinition) ValidateDelete() (admission.Warnings, error) {
- return admission.Warnings{}, nil
-}
diff --git a/api/v1alpha1/apiv4definition_types.go b/api/v1alpha1/apiv4definition_types.go
index 1b98830c4..ca1732f70 100644
--- a/api/v1alpha1/apiv4definition_types.go
+++ b/api/v1alpha1/apiv4definition_types.go
@@ -22,15 +22,15 @@ import (
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/hash"
v4 "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/v4"
- "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/management"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/refs"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/uuid"
"github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
- "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/list"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
+const separator = "/"
+
// ApiV4DefinitionSpec defines the desired state of ApiDefinition.
// +kubebuilder:object:generate=true
type ApiV4DefinitionSpec struct {
@@ -43,7 +43,7 @@ type ApiV4DefinitionStatus struct {
v4.Status `json:",inline"`
}
-var _ custom.ApiDefinition = &ApiV4Definition{}
+var _ custom.ApiDefinitionResource = &ApiV4Definition{}
var _ custom.Status = &ApiDefinitionStatus{}
var _ custom.Spec = &ApiDefinitionV2Spec{}
@@ -73,12 +73,19 @@ func (api *ApiV4Definition) IsBeingDeleted() bool {
return !api.ObjectMeta.DeletionTimestamp.IsZero()
}
-// PickID returns the ID of the API definition, when a context has been defined at the spec level.
+func (api *ApiV4Definition) PopulateIDs(context custom.Context) {
+ api.Spec.ID = api.pickID(context)
+ api.Spec.CrossID = api.pickCrossID()
+ api.Spec.Pages = api.pickPageIDs()
+ api.Spec.Plans = api.pickPlanIDs()
+}
+
+// pickID returns the ID of the API definition, when a context has been defined at the spec level.
// The ID might be returned from the API status, meaning that the API is already known.
// If the API is unknown, the ID is either given from the spec if given,
// or generated from the API UID and the context key to ensure uniqueness
// in case the API is replicated on a same APIM instance.
-func (api *ApiV4Definition) PickID(mCtx *management.Context) string {
+func (api *ApiV4Definition) pickID(mCtx custom.Context) string {
if api.Status.ID != "" {
return api.Status.ID
}
@@ -88,13 +95,13 @@ func (api *ApiV4Definition) PickID(mCtx *management.Context) string {
}
if mCtx != nil {
- return uuid.FromStrings(api.PickCrossID(), mCtx.OrgId, mCtx.EnvId)
+ return uuid.FromStrings(api.pickCrossID(), mCtx.GetOrgID(), mCtx.GetEnvID())
}
return string(api.UID)
}
-func (api *ApiV4Definition) PickCrossID() string {
+func (api *ApiV4Definition) pickCrossID() string {
if api.Status.CrossID != "" {
return api.Status.CrossID
}
@@ -107,33 +114,22 @@ func (api *ApiV4Definition) PickCrossID() string {
return uuid.FromStrings(namespacedName.String())
}
-func (api *ApiV4Definition) PickPlanIDs() map[string]*v4.Plan {
+func (api *ApiV4Definition) pickPlanIDs() map[string]*v4.Plan {
plans := make(map[string]*v4.Plan, len(api.Spec.Plans))
for key, plan := range api.Spec.Plans {
p := plan.DeepCopy()
if id, ok := api.Status.Plans[key]; ok {
- p.Id = id
- } else if plan.Id == "" {
+ p.ID = id
+ } else if plan.ID == "" {
namespacedName := api.GetNamespacedName()
- p.Id = uuid.FromStrings(namespacedName.String(), key)
+ p.ID = uuid.FromStrings(namespacedName.String(), key)
}
plans[key] = p
}
return plans
}
-const separator = "/"
-
-// GetOrGenerateEmptyPlanCrossID For each plan, generate a CrossId from Api Id & Plan Name if not defined.
-func (api *ApiV4Definition) GetOrGenerateEmptyPlanCrossID() {
- for name, plan := range api.Spec.Plans {
- if plan.CrossId == "" {
- plan.CrossId = uuid.FromStrings(api.PickCrossID(), separator, name)
- }
- }
-}
-
-func (api *ApiV4Definition) PickPageIDs() map[string]*v4.Page {
+func (api *ApiV4Definition) pickPageIDs() map[string]*v4.Page {
pages := make(map[string]*v4.Page, len(api.Spec.Pages))
for name, page := range api.Spec.Pages {
p := page.DeepCopy()
@@ -155,18 +151,18 @@ func (api *ApiV4Definition) PickPageIDs() map[string]*v4.Page {
return pages
}
-// EnvID implements custom.ApiDefinition.
-func (api *ApiV4Definition) EnvID() string {
+// GetEnvID implements custom.ApiDefinition.
+func (api *ApiV4Definition) GetEnvID() string {
return api.Status.EnvID
}
-// ID implements custom.ApiDefinition.
-func (api *ApiV4Definition) ID() string {
+// GetID implements custom.ApiDefinition.
+func (api *ApiV4Definition) GetID() string {
return api.Status.ID
}
-// OrgID implements custom.ApiDefinition.
-func (api *ApiV4Definition) OrgID() string {
+// GetOrgID implements custom.ApiDefinition.
+func (api *ApiV4Definition) GetOrgID() string {
return api.Status.OrgID
}
@@ -202,6 +198,18 @@ func (api *ApiV4Definition) GetObjectMeta() *metav1.ObjectMeta {
return &api.ObjectMeta
}
+func (api *ApiV4Definition) GetContextPaths() ([]string, error) {
+ return api.Spec.GetContextPaths()
+}
+
+func (api *ApiV4Definition) GetDefinitionVersion() custom.ApiDefinitionVersion {
+ return custom.ApiV4
+}
+
+func (api *ApiV4Definition) GetDefinition() custom.ApiDefinition {
+ return &api.Spec.Api
+}
+
func (spec *ApiV4DefinitionSpec) Hash() string {
return hash.Calculate(spec)
}
@@ -248,14 +256,6 @@ type ApiV4DefinitionList struct {
Items []ApiV4Definition `json:"items"`
}
-func (l *ApiV4DefinitionList) GetItems() []list.Item {
- items := make([]list.Item, len(l.Items))
- for i := range l.Items {
- items[i] = &l.Items[i]
- }
- return items
-}
-
func init() {
SchemeBuilder.Register(&ApiV4Definition{}, &ApiV4DefinitionList{})
}
diff --git a/api/v1alpha1/apiv4definition_webhook.go b/api/v1alpha1/apiv4definition_webhook.go
deleted file mode 100644
index a0826358e..000000000
--- a/api/v1alpha1/apiv4definition_webhook.go
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
- *
- * 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 v1alpha1
-
-import (
- "context"
-
- commonMutate "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/common/mutate"
- wk "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/webhook"
- runtime "k8s.io/apimachinery/pkg/runtime"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/webhook"
- "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
-)
-
-var _ webhook.Defaulter = &ApiV4Definition{}
-var _ webhook.Validator = &ApiV4Definition{}
-
-func (api *ApiV4Definition) SetupWebhookWithManager(mgr ctrl.Manager) error {
- return ctrl.NewWebhookManagedBy(mgr).
- For(api).
- Complete()
-}
-
-func (api *ApiV4Definition) Default() {
- commonMutate.SetDefaults(api)
-}
-
-func (api *ApiV4Definition) ValidateCreate() (admission.Warnings, error) {
- return wk.ValidateApiV4(context.Background(), &api.Spec.Api, api.Name, api.Namespace, api.Spec.Context)
-}
-
-func (api *ApiV4Definition) ValidateUpdate(_ runtime.Object) (admission.Warnings, error) {
- return wk.ValidateApiV4(context.Background(), &api.Spec.Api, api.Name, api.Namespace, api.Spec.Context)
-}
-
-func (*ApiV4Definition) ValidateDelete() (admission.Warnings, error) {
- return admission.Warnings{}, nil
-}
diff --git a/api/v1alpha1/application_types.go b/api/v1alpha1/application_types.go
index e9ceadc8d..950e09901 100644
--- a/api/v1alpha1/application_types.go
+++ b/api/v1alpha1/application_types.go
@@ -27,6 +27,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
+var _ custom.ContextAwareResource = &Application{}
+
// Application is the main resource handled by the Kubernetes Operator
// +kubebuilder:object:generate=true
type ApplicationSpec struct {
@@ -87,10 +89,18 @@ func (app *Application) HasContext() bool {
return app.Spec.Context != nil
}
-func (app *Application) ID() string {
+func (app *Application) GetID() string {
return app.Status.ID
}
+func (app *Application) GetOrgID() string {
+ return app.Status.OrgID
+}
+
+func (app *Application) GetEnvID() string {
+ return app.Status.EnvID
+}
+
func (app *Application) DeepCopyResource() custom.Resource {
return app.DeepCopy()
}
diff --git a/api/v1alpha1/application_webhook.go b/api/v1alpha1/application_webhook.go
deleted file mode 100644
index eb350ea25..000000000
--- a/api/v1alpha1/application_webhook.go
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
- *
- * 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 v1alpha1
-
-import (
- commonMutate "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/common/mutate"
- runtime "k8s.io/apimachinery/pkg/runtime"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/webhook"
- "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
-)
-
-var _ webhook.Defaulter = &Application{}
-var _ webhook.Validator = &Application{}
-
-func (app *Application) SetupWebhookWithManager(mgr ctrl.Manager) error {
- return ctrl.NewWebhookManagedBy(mgr).
- For(app).
- Complete()
-}
-
-func (app *Application) Default() {
- commonMutate.SetDefaults(app)
-}
-
-func (app *Application) ValidateCreate() (admission.Warnings, error) {
- return admission.Warnings{}, nil
-}
-
-func (app *Application) ValidateUpdate(_ runtime.Object) (admission.Warnings, error) {
- return admission.Warnings{}, nil
-}
-
-func (*Application) ValidateDelete() (admission.Warnings, error) {
- return admission.Warnings{}, nil
-}
diff --git a/api/v1alpha1/managementcontext_types.go b/api/v1alpha1/managementcontext_types.go
index 2693d32d1..715274c11 100644
--- a/api/v1alpha1/managementcontext_types.go
+++ b/api/v1alpha1/managementcontext_types.go
@@ -17,21 +17,67 @@
package v1alpha1
import (
+ "fmt"
+
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/management"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/refs"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/hash"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
)
+var _ custom.ContextResource = &ManagementContext{}
+var _ custom.Spec = &ManagementContextSpec{}
+var _ custom.Status = &ManagementContextStatus{}
+
// ManagementContext represents the configuration for a specific environment
// +kubebuilder:object:generate=true
type ManagementContextSpec struct {
*management.Context `json:",inline"`
}
+// Hash implements custom.Spec.
+func (spec *ManagementContextSpec) Hash() string {
+ return hash.Calculate(spec)
+}
+
// ManagementContextStatus defines the observed state of an API Context.
type ManagementContextStatus struct {
}
+// DeepCopyFrom implements custom.Status.
+func (st *ManagementContextStatus) DeepCopyFrom(obj client.Object) error {
+ switch t := obj.(type) {
+ case *ManagementContext:
+ t.Status.DeepCopyInto(st)
+ return nil
+ default:
+ return fmt.Errorf("unknown type %T", t)
+ }
+}
+
+// DeepCopyTo implements custom.Status.
+func (st *ManagementContextStatus) DeepCopyTo(obj client.Object) error {
+ switch t := obj.(type) {
+ case *ManagementContext:
+ st.DeepCopyInto(&t.Status)
+ return nil
+ default:
+ return fmt.Errorf("unknown type %T", t)
+ }
+}
+
+// SetObservedGeneration implements custom.Status.
+func (st *ManagementContextStatus) SetObservedGeneration(g int64) {
+ // Not implemented
+}
+
+// SetProcessingStatus implements custom.Status.
+func (st *ManagementContextStatus) SetProcessingStatus(status custom.ProcessingStatus) {
+ // Not implemented
+}
+
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
@@ -45,6 +91,56 @@ type ManagementContext struct {
Status ManagementContextStatus `json:"status,omitempty"`
}
+// DeepCopyResource implements custom.Context.
+func (ctx *ManagementContext) DeepCopyResource() custom.Resource {
+ return ctx.DeepCopy()
+}
+
+// GetSpec implements custom.Context.
+func (ctx *ManagementContext) GetSpec() custom.Spec {
+ return &ctx.Spec
+}
+
+// GetStatus implements custom.Context.
+func (ctx *ManagementContext) GetStatus() custom.Status {
+ return &ctx.Status
+}
+
+// GetAuth implements custom.Context.
+func (ctx *ManagementContext) GetAuth() custom.Auth {
+ return ctx.Spec.Context.Auth
+}
+
+// GetEnvID implements custom.Context.
+func (ctx *ManagementContext) GetEnvID() string {
+ return ctx.Spec.EnvID
+}
+
+// GetOrgID implements custom.Context.
+func (ctx *ManagementContext) GetOrgID() string {
+ return ctx.Spec.OrgID
+}
+
+// GetSecretRef implements custom.Context.
+func (ctx *ManagementContext) GetSecretRef() custom.ResourceRef {
+ return ctx.Spec.SecretRef()
+}
+
+// GetURL implements custom.Context.
+func (ctx *ManagementContext) GetURL() string {
+ return ctx.Spec.BaseUrl
+}
+
+// HasAuthentication implements custom.Context.
+func (ctx *ManagementContext) HasAuthentication() bool {
+ return ctx.Spec.Auth != nil
+}
+
+// HasSecretRef implements custom.Context.
+func (ctx *ManagementContext) HasSecretRef() bool {
+ return ctx.HasAuthentication() && ctx.Spec.Auth.SecretRef != nil
+}
+
func (ctx *ManagementContext) GetNamespacedName() *refs.NamespacedName {
return &refs.NamespacedName{Namespace: ctx.Namespace, Name: ctx.Name}
}
diff --git a/api/v1alpha1/managementcontext_webhook.go b/api/v1alpha1/managementcontext_webhook.go
deleted file mode 100644
index 8c0a08a74..000000000
--- a/api/v1alpha1/managementcontext_webhook.go
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
- *
- * 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 v1alpha1
-
-import (
- "context"
- "fmt"
-
- "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/refs"
- wk "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/webhook"
- "github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s"
- corev1 "k8s.io/api/core/v1"
- runtime "k8s.io/apimachinery/pkg/runtime"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/webhook"
- "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
-)
-
-var _ webhook.Defaulter = &ManagementContext{}
-var _ webhook.Validator = &ManagementContext{}
-
-func (ctx *ManagementContext) SetupWebhookWithManager(mgr ctrl.Manager) error {
- return ctrl.NewWebhookManagedBy(mgr).
- For(ctx).
- Complete()
-}
-
-func (ctx *ManagementContext) Default() {}
-
-func (ctx *ManagementContext) ValidateCreate() (admission.Warnings, error) {
- return validateManagementContext(ctx)
-}
-
-func (ctx *ManagementContext) ValidateUpdate(_ runtime.Object) (admission.Warnings, error) {
- return validateManagementContext(ctx)
-}
-
-func (*ManagementContext) ValidateDelete() (admission.Warnings, error) {
- return admission.Warnings{}, nil
-}
-
-func validateManagementContext(ctx *ManagementContext) (admission.Warnings, error) {
- // Make sure the secret exist
- if ctx.Spec.HasSecretRef() {
- secret := new(corev1.Secret)
- err := k8s.GetClient().Get(context.Background(), ctx.Spec.SecretRef().NamespacedName(), secret)
- if err != nil {
- return admission.Warnings{}, fmt.Errorf("can't create management context [%s] because it is using "+
- "sercret [%v] that doesn't exist in the cluster", ctx.Name, ctx.Spec.SecretRef())
- }
- }
-
- ctxRef := refs.NewNamespacedName(ctx.Namespace, ctx.Name)
- if err := wk.CheckAPIMAvailability(&ctxRef); err != nil {
- return admission.Warnings{err.Error()}, nil //nolint:nilerr // changed to warning
- }
-
- return admission.Warnings{}, nil
-}
diff --git a/controllers/apim/apidefinition/controller.go b/controllers/apim/apidefinition/controller.go
index cf9a92191..32114edc1 100644
--- a/controllers/apim/apidefinition/controller.go
+++ b/controllers/apim/apidefinition/controller.go
@@ -41,7 +41,7 @@ const requeueAfterTime = time.Second * 5
func Reconcile(
ctx context.Context,
- apiDefinition custom.ApiDefinition,
+ apiDefinition custom.ApiDefinitionResource,
r record.EventRecorder,
) (ctrl.Result, error) {
logger := log.FromContext(ctx)
@@ -66,7 +66,7 @@ func Reconcile(
func reconcileApiDefinition(
ctx context.Context,
- apiDefinition custom.ApiDefinition,
+ apiDefinition custom.ApiDefinitionResource,
events *event.Recorder,
) (ctrl.Result, error) {
logger := log.FromContext(ctx)
diff --git a/controllers/apim/apidefinition/internal/api_definition_template.go b/controllers/apim/apidefinition/internal/api_definition_template.go
index 589d1f42f..9ac4789d8 100644
--- a/controllers/apim/apidefinition/internal/api_definition_template.go
+++ b/controllers/apim/apidefinition/internal/api_definition_template.go
@@ -33,7 +33,7 @@ import (
// First return value defines if we should requeue or not.
func SyncApiDefinitionTemplate(
ctx context.Context,
- api custom.ApiDefinition, ns string) error {
+ api custom.ApiDefinitionResource, ns string) error {
// We are first looking if the template is in deletion phase, the Kubernetes API marks the object for
// deletion by populating .metadata.deletionTimestamp
if !api.GetDeletionTimestamp().IsZero() {
diff --git a/controllers/apim/apidefinition/internal/config_map.go b/controllers/apim/apidefinition/internal/config_map.go
index ddc9c74b8..7a6eb1aab 100644
--- a/controllers/apim/apidefinition/internal/config_map.go
+++ b/controllers/apim/apidefinition/internal/config_map.go
@@ -38,8 +38,8 @@ const (
gioTypeKey = "gio-type"
orgKey = "organizationId"
envKey = "environmentId"
- defaultEnvId = "DEFAULT"
- defaultOrgId = "DEFAULT"
+ defaultEnvID = "DEFAULT"
+ defaultOrgID = "DEFAULT"
)
func updateConfigMap(
@@ -63,7 +63,7 @@ func updateConfigMap(
func saveConfigMap(
ctx context.Context,
- apiDefinition custom.ApiDefinition,
+ apiDefinition custom.ApiDefinitionResource,
) error {
// Create config map with some specific metadata that will be used to check changes across 'Update' events.
cm := &v1.ConfigMap{}
@@ -93,12 +93,12 @@ func saveConfigMap(
definitionVersionKey: apiDefinition.GetResourceVersion(),
}
- if apiDefinition.OrgID() != "" {
- cm.Data[orgKey] = apiDefinition.OrgID()
- cm.Data[envKey] = apiDefinition.EnvID()
+ if apiDefinition.GetOrgID() != "" {
+ cm.Data[orgKey] = apiDefinition.GetOrgID()
+ cm.Data[envKey] = apiDefinition.GetEnvID()
} else {
- cm.Data[orgKey] = defaultOrgId
- cm.Data[envKey] = defaultEnvId
+ cm.Data[orgKey] = defaultOrgID
+ cm.Data[envKey] = defaultEnvID
}
var payload any
diff --git a/controllers/apim/apidefinition/internal/delete.go b/controllers/apim/apidefinition/internal/delete.go
index 7dade1124..bb2c78248 100644
--- a/controllers/apim/apidefinition/internal/delete.go
+++ b/controllers/apim/apidefinition/internal/delete.go
@@ -25,7 +25,7 @@ import (
util "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
-func Delete(ctx context.Context, api custom.ApiDefinition) error {
+func Delete(ctx context.Context, api custom.ApiDefinitionResource) error {
if !util.ContainsFinalizer(api, keys.ApiDefinitionFinalizer) {
return nil
}
@@ -41,17 +41,17 @@ func Delete(ctx context.Context, api custom.ApiDefinition) error {
return nil
}
-func deleteWithContext(ctx context.Context, api custom.ApiDefinition) error {
- apim, err := apim.FromContextRef(ctx, api.ContextRef())
+func deleteWithContext(ctx context.Context, api custom.ApiDefinitionResource) error {
+ apim, err := apim.FromContextRef(ctx, api.ContextRef(), api.GetNamespace())
if err != nil {
return err
}
switch {
- case api.Version() == custom.ApiV2:
- return errors.IgnoreNotFound(apim.APIs.DeleteV2(api.ID()))
- case api.Version() == custom.ApiV4:
- return errors.IgnoreNotFound(apim.APIs.DeleteV4(api.ID()))
+ case api.GetDefinitionVersion() == custom.ApiV2:
+ return errors.IgnoreNotFound(apim.APIs.DeleteV2(api.GetID()))
+ case api.GetDefinitionVersion() == custom.ApiV4:
+ return errors.IgnoreNotFound(apim.APIs.DeleteV4(api.GetID()))
default:
- return fmt.Errorf("unknown version %s", api.Version())
+ return fmt.Errorf("unknown version %s", api.GetDefinitionVersion())
}
}
diff --git a/controllers/apim/apidefinition/internal/helper.go b/controllers/apim/apidefinition/internal/helper.go
index f80ffbb6e..c2af5ef1c 100644
--- a/controllers/apim/apidefinition/internal/helper.go
+++ b/controllers/apim/apidefinition/internal/helper.go
@@ -17,48 +17,16 @@ package internal
import (
"github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
apimModel "github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim/model"
- "github.com/gravitee-io/gravitee-kubernetes-operator/internal/uuid"
)
-const separator = "/"
-
-// For each plan, generate a CrossId from Api Id & Plan Name if not defined.
-func generateEmptyPlanCrossIds(spec *v1alpha1.ApiDefinitionV2Spec) {
- plans := spec.Plans
-
- for _, plan := range plans {
- if plan.CrossId == "" {
- plan.CrossId = uuid.FromStrings(spec.ID, separator, plan.Name)
- }
- }
-}
-
-func generatePageIDs(api *v1alpha1.ApiDefinition) {
- spec := &api.Spec
- pages := spec.Pages
- for name, page := range pages {
- page.API = spec.ID
- apiName := api.GetNamespacedName().String()
- if page.CrossID == "" {
- page.CrossID = uuid.FromStrings(apiName, separator, name)
- }
- if page.ID == "" {
- page.ID = uuid.FromStrings(spec.ID, separator, name)
- }
- if page.Parent != "" {
- page.ParentID = uuid.FromStrings(spec.ID, separator, page.Parent)
- }
- }
-}
-
// Retrieve the plan ids from the management apiEntity.
-func retrieveMgmtPlanIds(spec *v1alpha1.ApiDefinitionV2Spec, mgmtApi *apimModel.ApiEntity) {
+func retrieveMgmtPlanIDs(spec *v1alpha1.ApiDefinitionV2Spec, mgmtApi *apimModel.ApiEntity) {
plans := spec.Plans
for _, plan := range plans {
for _, mgmtPlan := range mgmtApi.Plans {
- if plan.CrossId == mgmtPlan.CrossId {
- plan.Id = mgmtPlan.Id
+ if plan.CrossID == mgmtPlan.CrossID {
+ plan.ID = mgmtPlan.ID
plan.Api = mgmtPlan.Api
}
}
diff --git a/controllers/apim/apidefinition/internal/update.go b/controllers/apim/apidefinition/internal/update.go
index d1fd616af..d271e7703 100644
--- a/controllers/apim/apidefinition/internal/update.go
+++ b/controllers/apim/apidefinition/internal/update.go
@@ -49,14 +49,14 @@ func createOrUpdateV2(ctx context.Context, apiDefinition *v1alpha1.ApiDefinition
spec := &cp.Spec
formerStatus := cp.Status
- spec.ID = cp.PickID()
spec.SetDefinitionContext()
- generateEmptyPlanCrossIds(spec)
if err := resolveResources(ctx, spec.Resources); err != nil {
return err
}
+ cp.PopulateIDs(nil)
+
if !apiDefinition.HasContext() {
if !spec.IsLocal {
return errors.NewUnrecoverableError("a context is required when setting local to false")
@@ -71,14 +71,11 @@ func createOrUpdateV2(ctx context.Context, apiDefinition *v1alpha1.ApiDefinition
log.FromContext(ctx).Info("Syncing API with APIM")
- apim, apimErr := apim.FromContextRef(ctx, spec.Context)
+ apim, apimErr := apim.FromContextRef(ctx, spec.Context, apiDefinition.GetNamespace())
if apimErr != nil {
return apimErr
}
- generatePageIDs(cp)
- spec.CrossID = cp.PickCrossID()
-
_, findErr := apim.APIs.GetByCrossID(spec.CrossID)
if errors.IgnoreNotFound(findErr) != nil {
return errors.NewContextError(findErr)
@@ -100,10 +97,10 @@ func createOrUpdateV2(ctx context.Context, apiDefinition *v1alpha1.ApiDefinition
apiDefinition.Status.EnvID = apim.EnvID()
apiDefinition.Status.OrgID = apim.OrgID()
apiDefinition.Status.State = base.ApiState(mgmtApi.State)
- retrieveMgmtPlanIds(spec, mgmtApi)
+ retrieveMgmtPlanIDs(spec, mgmtApi)
if mgmtApi.ShouldSetKubernetesContext() {
- if err := apim.APIs.SetKubernetesContext(apiDefinition.ID()); err != nil {
+ if err := apim.APIs.SetKubernetesContext(apiDefinition.GetID()); err != nil {
return errors.NewContextError(err)
}
}
@@ -115,11 +112,11 @@ func createOrUpdateV2(ctx context.Context, apiDefinition *v1alpha1.ApiDefinition
if err := deleteConfigMap(ctx, apiDefinition); err != nil {
return err
}
- if err := apim.APIs.Deploy(apiDefinition.ID()); err != nil {
+ if err := apim.APIs.Deploy(apiDefinition.GetID()); err != nil {
return err
}
if formerStatus.State != spec.State {
- return apim.APIs.UpdateState(apiDefinition.ID(), model.ApiStateToAction(spec.State))
+ return apim.APIs.UpdateState(apiDefinition.GetID(), model.ApiStateToAction(spec.State))
}
return nil
@@ -135,18 +132,15 @@ func createOrUpdateV4(ctx context.Context, apiDefinition *v1alpha1.ApiV4Definiti
return err
}
- spec.CrossID = cp.PickCrossID()
- spec.Plans = cp.PickPlanIDs()
- spec.Pages = cp.PickPageIDs()
spec.DefinitionContext = v4.NewDefaultKubernetesContext().MergeWith(spec.DefinitionContext)
if spec.Context != nil {
log.FromContext(ctx).Info("Syncing API with APIM")
- apim, err := apim.FromContextRef(ctx, spec.Context)
+ apim, err := apim.FromContextRef(ctx, spec.Context, apiDefinition.GetNamespace())
if err != nil {
return err
}
- spec.ID = cp.PickID(apim.Context)
+ cp.PopulateIDs(apim.Context)
status, err := apim.APIs.ImportV4(&spec.Api)
if err != nil {
return err
@@ -154,7 +148,7 @@ func createOrUpdateV4(ctx context.Context, apiDefinition *v1alpha1.ApiV4Definiti
apiDefinition.Status.Status = *status
log.FromContext(ctx).WithValues("id", spec.ID).Info("API successfully synced with APIM")
} else {
- spec.ID = cp.PickID(nil)
+ cp.PopulateIDs(nil)
}
if spec.DefinitionContext.SyncFrom == v4.OriginManagement || spec.State == base.StateStopped {
diff --git a/controllers/apim/application/internal/delete.go b/controllers/apim/application/internal/delete.go
index 175947bb8..1c563d96f 100644
--- a/controllers/apim/application/internal/delete.go
+++ b/controllers/apim/application/internal/delete.go
@@ -32,7 +32,7 @@ func Delete(
return nil
}
- apim, apimErr := apim.FromContextRef(ctx, application.Spec.Context)
+ apim, apimErr := apim.FromContextRef(ctx, application.Spec.Context, application.GetNamespace())
if apimErr != nil {
return apimErr
}
diff --git a/controllers/apim/application/internal/update.go b/controllers/apim/application/internal/update.go
index 05dcf4ae6..50ac1830b 100644
--- a/controllers/apim/application/internal/update.go
+++ b/controllers/apim/application/internal/update.go
@@ -27,7 +27,7 @@ func CreateOrUpdate(ctx context.Context, application *v1alpha1.Application) erro
spec.Origin = "KUBERNETES"
spec.ID = application.Status.ID
- apim, err := apim.FromContextRef(ctx, spec.Context)
+ apim, err := apim.FromContextRef(ctx, spec.Context, application.GetNamespace())
if err != nil {
return err
}
diff --git a/docs/api/reference.md b/docs/api/reference.md
index fff46daf7..51d9edf1f 100644
--- a/docs/api/reference.md
+++ b/docs/api/reference.md
@@ -7091,6 +7091,14 @@ ApiV4DefinitionStatus defines the observed state of API Definition.
The environment ID, if a management context has been defined to sync with an APIM instance
false |
+
+ errors |
+ object |
+
+ When API has been created regardless of errors, this field is
+used to persist the error message encountered during admission
+ |
+ false |
id |
string |
@@ -7142,6 +7150,45 @@ to sync the API with an APIM instance
+
+### ApiV4Definition.status.errors
+[Go to parent definition](#apiv4definitionstatus)
+
+
+
+When API has been created regardless of errors, this field is
+used to persist the error message encountered during admission
+
+
+
+
+ Name |
+ Type |
+ Description |
+ Required |
+
+
+
+ severe |
+ []string |
+
+ severe errors do not pass admission and will block reconcile
+hence, this field should always be during the admission phase
+and is very unlikely to be persisted in the status
+ |
+ false |
+
+ warning |
+ []string |
+
+ warning errors do not block object reconciliation,
+most of the time because the value is ignored or defaulted
+when the API gets synced with APIM
+ |
+ false |
+
+
+
## ApiResource
[gravitee.io/v1alpha1](#graviteeiov1alpha1)
@@ -7322,7 +7369,7 @@ Application is the main resource handled by the Kubernetes Operator
clientId |
string |
- The ClientId identifying the application. This field is required when subscribing to an OAUTH2 / JWT plan.
+ The ClientID identifying the application. This field is required when subscribing to an OAUTH2 / JWT plan.
|
false |
@@ -7504,7 +7551,7 @@ Application settings
client_id |
string |
- ClientId is the client id of the application
+ ClientID is the client id of the application
|
false |
diff --git a/examples/apim/api_definition/v2/api-with-context.yml b/examples/apim/api_definition/v2/api-with-context.yml
index 94ef1acc7..d9f4f21ce 100644
--- a/examples/apim/api_definition/v2/api-with-context.yml
+++ b/examples/apim/api_definition/v2/api-with-context.yml
@@ -21,7 +21,6 @@ spec:
name: "Echo API"
contextRef:
name: "dev-ctx"
- namespace: "default"
version: "1.1"
description: "Gravitee Kubernetes Operator sample"
plans:
diff --git a/examples/management_context/debug/management-context-secret.yml b/examples/management_context/debug/management-context-secret.yml
new file mode 100644
index 000000000..6c3f4514d
--- /dev/null
+++ b/examples/management_context/debug/management-context-secret.yml
@@ -0,0 +1,22 @@
+# Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+#
+# 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.
+
+apiVersion: v1
+kind: Secret
+metadata:
+ name: apim-context-credentials
+data:
+ password: YWRtaW4=
+ username: YWRtaW4=
+
diff --git a/examples/management_context/debug/management-context-with-secret-ref.yml b/examples/management_context/debug/management-context-with-secret-ref.yml
new file mode 100644
index 000000000..d5b49fe9a
--- /dev/null
+++ b/examples/management_context/debug/management-context-with-secret-ref.yml
@@ -0,0 +1,25 @@
+# Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+#
+# 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.
+apiVersion: gravitee.io/v1alpha1
+kind: ManagementContext
+metadata:
+ name: dev-ctx
+spec:
+ baseUrl: http://localhost:8083
+ environmentId: DEFAULT
+ organizationId: DEFAULT
+ auth:
+ secretRef:
+ name: apim-context-credentials
+
diff --git a/helm/gko/crds/gravitee.io_apiv4definitions.yaml b/helm/gko/crds/gravitee.io_apiv4definitions.yaml
index 72f6c597c..363d9ece4 100644
--- a/helm/gko/crds/gravitee.io_apiv4definitions.yaml
+++ b/helm/gko/crds/gravitee.io_apiv4definitions.yaml
@@ -1278,6 +1278,28 @@ spec:
description: The environment ID, if a management context has been
defined to sync with an APIM instance
type: string
+ errors:
+ description: |-
+ When API has been created regardless of errors, this field is
+ used to persist the error message encountered during admission
+ properties:
+ severe:
+ description: |-
+ severe errors do not pass admission and will block reconcile
+ hence, this field should always be during the admission phase
+ and is very unlikely to be persisted in the status
+ items:
+ type: string
+ type: array
+ warning:
+ description: |-
+ warning errors do not block object reconciliation,
+ most of the time because the value is ignored or defaulted
+ when the API gets synced with APIM
+ items:
+ type: string
+ type: array
+ type: object
id:
description: The ID of the API definition in the Gravitee API Management
instance (if an API context has been configured).
diff --git a/helm/gko/crds/gravitee.io_applications.yaml b/helm/gko/crds/gravitee.io_applications.yaml
index 34b450c8d..356e18524 100644
--- a/helm/gko/crds/gravitee.io_applications.yaml
+++ b/helm/gko/crds/gravitee.io_applications.yaml
@@ -75,7 +75,7 @@ spec:
when displaying it on the portal
type: string
clientId:
- description: The ClientId identifying the application. This field
+ description: The ClientID identifying the application. This field
is required when subscribing to an OAUTH2 / JWT plan.
type: string
contextRef:
@@ -164,7 +164,7 @@ spec:
app:
properties:
client_id:
- description: ClientId is the client id of the application
+ description: ClientID is the client id of the application
type: string
type:
description: Application Type
diff --git a/internal/admission/webhook/managementcontext.go b/internal/admission/api/base/validate.go
similarity index 53%
rename from internal/admission/webhook/managementcontext.go
rename to internal/admission/api/base/validate.go
index 104e01670..a05c92569 100644
--- a/internal/admission/webhook/managementcontext.go
+++ b/internal/admission/api/base/validate.go
@@ -12,31 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package webhook
+package base
import (
"context"
- "errors"
- "fmt"
- "net"
- "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/refs"
- "github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim"
- "github.com/gravitee-io/gravitee-kubernetes-operator/internal/uuid"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/ctxref"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/errors"
+ "k8s.io/apimachinery/pkg/runtime"
)
-func CheckAPIMAvailability(ctxRef *refs.NamespacedName) error {
- cli, err := apim.FromContextRef(context.Background(), ctxRef)
- if err != nil {
- return fmt.Errorf("can't create apim client for this management context [%s]", ctxRef.Name)
- }
+func ValidateCreate(ctx context.Context, obj runtime.Object) *errors.AdmissionErrors {
+ errs := errors.NewAdmissionErrors()
- _, err = cli.APIs.GetV4ByID(uuid.NewV4String())
+ errs.Add(ctxref.Validate(ctx, obj))
- var opError *net.OpError
- if errors.As(err, &opError) {
- return fmt.Errorf("unable to reach APIM, [%s] is not available", cli.Context.BaseUrl)
- }
+ return errs
+}
- return nil
+func ValidateUpdate(ctx context.Context, obj runtime.Object) *errors.AdmissionErrors {
+ return ValidateCreate(ctx, obj)
}
diff --git a/internal/admission/api/v2/ctrl.go b/internal/admission/api/v2/ctrl.go
new file mode 100644
index 000000000..d8e1b516a
--- /dev/null
+++ b/internal/admission/api/v2/ctrl.go
@@ -0,0 +1,69 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 v2
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ ctrl "sigs.k8s.io/controller-runtime"
+)
+
+var _ webhook.CustomValidator = AdmissionCtrl{}
+var _ webhook.CustomDefaulter = AdmissionCtrl{}
+
+type AdmissionCtrl struct{}
+
+func (a AdmissionCtrl) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewWebhookManagedBy(mgr).
+ For(&v1alpha1.ApiDefinition{}).
+ WithValidator(a).
+ WithDefaulter(a).
+ Complete()
+}
+
+// Default implements admission.CustomDefaulter.
+func (a AdmissionCtrl) Default(ctx context.Context, obj runtime.Object) error {
+ return nil
+}
+
+// ValidateCreate implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateCreate(
+ ctx context.Context,
+ obj runtime.Object,
+) (admission.Warnings, error) {
+ return validateCreate(ctx, obj).Map()
+}
+
+// ValidateUpdate implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateUpdate(
+ ctx context.Context,
+ oldObj runtime.Object,
+ newObj runtime.Object,
+) (admission.Warnings, error) {
+ return a.ValidateCreate(ctx, newObj)
+}
+
+// ValidateDelete implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateDelete(
+ ctx context.Context,
+ obj runtime.Object,
+) (admission.Warnings, error) {
+ return admission.Warnings{}, nil
+}
diff --git a/internal/admission/api/v2/validate.go b/internal/admission/api/v2/validate.go
new file mode 100644
index 000000000..baa8a291a
--- /dev/null
+++ b/internal/admission/api/v2/validate.go
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 v2
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/base"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/errors"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+func validateCreate(ctx context.Context, obj runtime.Object) *errors.AdmissionErrors {
+ return base.ValidateCreate(ctx, obj)
+}
diff --git a/internal/admission/api/v4/ctrl.go b/internal/admission/api/v4/ctrl.go
new file mode 100644
index 000000000..e3ac23d42
--- /dev/null
+++ b/internal/admission/api/v4/ctrl.go
@@ -0,0 +1,69 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 v4
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ ctrl "sigs.k8s.io/controller-runtime"
+)
+
+var _ webhook.CustomValidator = AdmissionCtrl{}
+var _ webhook.CustomDefaulter = AdmissionCtrl{}
+
+type AdmissionCtrl struct{}
+
+func (a AdmissionCtrl) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewWebhookManagedBy(mgr).
+ For(&v1alpha1.ApiV4Definition{}).
+ WithValidator(a).
+ WithDefaulter(a).
+ Complete()
+}
+
+// Default implements admission.CustomDefaulter.
+func (a AdmissionCtrl) Default(ctx context.Context, obj runtime.Object) error {
+ return nil
+}
+
+// ValidateCreate implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateCreate(
+ ctx context.Context,
+ obj runtime.Object,
+) (admission.Warnings, error) {
+ return validateCreate(ctx, obj).Map()
+}
+
+// ValidateDelete implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateDelete(
+ ctx context.Context,
+ obj runtime.Object,
+) (admission.Warnings, error) {
+ return admission.Warnings{}, nil
+}
+
+// ValidateUpdate implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateUpdate(
+ ctx context.Context,
+ oldObj runtime.Object,
+ newObj runtime.Object,
+) (admission.Warnings, error) {
+ return a.ValidateCreate(ctx, newObj)
+}
diff --git a/internal/admission/api/v4/validate.go b/internal/admission/api/v4/validate.go
new file mode 100644
index 000000000..3a0a831da
--- /dev/null
+++ b/internal/admission/api/v4/validate.go
@@ -0,0 +1,159 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 v4
+
+import (
+ "context"
+ "net/url"
+ "slices"
+
+ v4 "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/v4"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/base"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/env"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/errors"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s/dynamic"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func validateCreate(ctx context.Context, obj runtime.Object) *errors.AdmissionErrors {
+ errs := errors.NewAdmissionErrors()
+ if api, ok := obj.(custom.ApiDefinitionResource); ok {
+ errs = errs.MergeWith(base.ValidateCreate(ctx, obj))
+ if errs.IsSevere() {
+ return errs
+ }
+ errs.Add(validateNoConflictingPath(ctx, api))
+ if errs.IsSevere() {
+ return errs
+ }
+ if api.HasContext() {
+ errs = errs.MergeWith(validateDryRun(ctx, api))
+ }
+ }
+ return errs
+}
+
+// TODO this should be move to base once implemented for v2
+func validateNoConflictingPath(ctx context.Context, api custom.ApiDefinitionResource) *errors.AdmissionError {
+ apiPaths, err := api.GetContextPaths()
+ if err != nil {
+ return errors.NewSevere(err.Error())
+ }
+ existingPaths, err := getExistingPaths(ctx, api)
+ if err != nil {
+ return errors.NewSevere(err.Error())
+ }
+ for _, apiPath := range apiPaths {
+ if _, pErr := url.Parse(apiPath); pErr != nil {
+ return errors.NewSevere(
+ "path [%s] is invalid",
+ apiPath,
+ )
+ }
+ if slices.Contains(existingPaths, apiPath) {
+ return errors.NewSevere(
+ "invalid API context path [%s]. Another API with the same path already exists",
+ apiPath,
+ )
+ }
+ }
+ return nil
+}
+
+func validateDryRun(ctx context.Context, api custom.ApiDefinitionResource) *errors.AdmissionErrors {
+ errs := errors.NewAdmissionErrors()
+
+ cp, _ := api.DeepCopyResource().(custom.ApiDefinitionResource)
+
+ apim, err := apim.FromContextRef(ctx, cp.ContextRef(), cp.GetNamespace())
+ if err != nil {
+ errs.AddSevere(err.Error())
+ }
+
+ cp.PopulateIDs(apim.Context)
+
+ impl, ok := cp.GetDefinition().(*v4.Api)
+ if !ok {
+ errs.AddSevere("unable to call dry run import because api is not a v4 API")
+ }
+
+ status, err := apim.APIs.DryRunImportV4(impl)
+ if err != nil {
+ errs.AddSevere(err.Error())
+ return errs
+ }
+ for _, severe := range status.Errors.Severe {
+ errs.AddSevere(severe)
+ }
+ if errs.IsSevere() {
+ return errs
+ }
+ for _, warning := range status.Errors.Warning {
+ errs.AddWarning(warning)
+ }
+ return errs
+}
+
+func getExistingPaths(ctx context.Context, api custom.ApiDefinitionResource) ([]string, error) {
+ existingPaths := make([]string, 0)
+ unstructuredList, err := getListOfExistingApis(ctx, api.GetNamespace())
+ if err != nil {
+ return existingPaths, err
+ }
+
+ for _, item := range unstructuredList.Items {
+ converted, cErr := dynamic.Convert(item.Object["spec"], new(v4.Api))
+ if cErr != nil {
+ return existingPaths, cErr
+ }
+ convertedPaths, pErr := converted.GetContextPaths()
+ if pErr != nil {
+ return existingPaths, pErr
+ }
+ if !isCurrentApi(item, api) {
+ existingPaths = append(existingPaths, convertedPaths...)
+ }
+ }
+ return existingPaths, nil
+}
+
+func isCurrentApi(item unstructured.Unstructured, api custom.ApiDefinitionResource) bool {
+ return api.GetName() == item.Object["metadata"].(map[string]interface{})["name"] &&
+ api.GetNamespace() == item.Object["metadata"].(map[string]interface{})["namespace"]
+}
+
+func getListOfExistingApis(ctx context.Context, ns string) (*unstructured.UnstructuredList, error) {
+ gvr := schema.GroupVersionResource{
+ Group: "gravitee.io",
+ Version: "v1alpha1",
+ Resource: "apiv4definitions",
+ }
+ if !env.Config.CheckApiContextPathConflictInCluster {
+ return dynamic.GetClient().
+ Resource(gvr).
+ Namespace(ns).
+ List(ctx, metav1.ListOptions{})
+ } else {
+ return dynamic.GetClient().
+ Resource(gvr).
+ List(ctx, metav1.ListOptions{})
+ }
+}
diff --git a/internal/admission/application/ctrl.go b/internal/admission/application/ctrl.go
new file mode 100644
index 000000000..cb2b5d988
--- /dev/null
+++ b/internal/admission/application/ctrl.go
@@ -0,0 +1,68 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 application
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ ctrl "sigs.k8s.io/controller-runtime"
+)
+
+var _ webhook.CustomValidator = AdmissionCtrl{}
+var _ webhook.CustomDefaulter = AdmissionCtrl{}
+
+func (a AdmissionCtrl) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewWebhookManagedBy(mgr).
+ For(&v1alpha1.ManagementContext{}).
+ WithValidator(a).
+ WithDefaulter(a).
+ Complete()
+}
+
+type AdmissionCtrl struct{}
+
+// Default implements admission.CustomDefaulter.
+func (a AdmissionCtrl) Default(ctx context.Context, obj runtime.Object) error {
+ return nil
+}
+
+// ValidateCreate implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateCreate(
+ ctx context.Context,
+ obj runtime.Object,
+) (admission.Warnings, error) {
+ return validateCreate(ctx, obj).Map()
+}
+
+// ValidateDelete implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateDelete(
+ ctx context.Context, obj runtime.Object,
+) (admission.Warnings, error) {
+ return admission.Warnings{}, nil
+}
+
+// ValidateUpdate implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateUpdate(
+ ctx context.Context,
+ oldObj runtime.Object,
+ newObj runtime.Object,
+) (admission.Warnings, error) {
+ return validateCreate(ctx, newObj).Map()
+}
diff --git a/internal/admission/application/validate.go b/internal/admission/application/validate.go
new file mode 100644
index 000000000..149bcd659
--- /dev/null
+++ b/internal/admission/application/validate.go
@@ -0,0 +1,32 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 application
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/ctxref"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/errors"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+func validateCreate(ctx context.Context, obj runtime.Object) *errors.AdmissionErrors {
+ errs := errors.NewAdmissionErrors()
+ if app, ok := obj.(custom.ApplicationResource); ok {
+ errs.Add(ctxref.Validate(ctx, app))
+ }
+ return errs
+}
diff --git a/internal/admission/ctxref/validate.go b/internal/admission/ctxref/validate.go
new file mode 100644
index 000000000..6ed5e05db
--- /dev/null
+++ b/internal/admission/ctxref/validate.go
@@ -0,0 +1,45 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 ctxref
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/errors"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s/dynamic"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+func Validate(ctx context.Context, obj runtime.Object) *errors.AdmissionError {
+ if ctxAware, ok := obj.(custom.ContextAwareResource); ok {
+ if ctxAware.HasContext() {
+ return validateContextRefExists(ctx, ctxAware)
+ }
+ }
+ return nil
+}
+
+func validateContextRefExists(ctx context.Context, ctxAware custom.ContextAwareResource) *errors.AdmissionError {
+ ctxRef := ctxAware.ContextRef()
+ if err := dynamic.ExpectResolvedContext(ctx, ctxRef, ctxAware.GetNamespace()); err != nil {
+ return errors.NewSevere(
+ "resource [%s] references management context [%v] that doesn't exist in the cluster",
+ ctxAware.GetName(),
+ ctxRef,
+ )
+ }
+ return nil
+}
diff --git a/internal/admission/mctx/ctrl.go b/internal/admission/mctx/ctrl.go
new file mode 100644
index 000000000..4deed1f5a
--- /dev/null
+++ b/internal/admission/mctx/ctrl.go
@@ -0,0 +1,68 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 mctx
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ ctrl "sigs.k8s.io/controller-runtime"
+)
+
+var _ webhook.CustomValidator = AdmissionCtrl{}
+var _ webhook.CustomDefaulter = AdmissionCtrl{}
+
+func (a AdmissionCtrl) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewWebhookManagedBy(mgr).
+ For(&v1alpha1.ManagementContext{}).
+ WithValidator(a).
+ WithDefaulter(a).
+ Complete()
+}
+
+type AdmissionCtrl struct{}
+
+// Default implements admission.CustomDefaulter.
+func (a AdmissionCtrl) Default(ctx context.Context, obj runtime.Object) error {
+ return nil
+}
+
+// ValidateCreate implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateCreate(
+ ctx context.Context,
+ obj runtime.Object,
+) (admission.Warnings, error) {
+ return validateCreate(ctx, obj).Map()
+}
+
+// ValidateDelete implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateDelete(
+ ctx context.Context, obj runtime.Object,
+) (admission.Warnings, error) {
+ return admission.Warnings{}, nil
+}
+
+// ValidateUpdate implements admission.CustomValidator.
+func (a AdmissionCtrl) ValidateUpdate(
+ ctx context.Context,
+ oldObj runtime.Object,
+ newObj runtime.Object,
+) (admission.Warnings, error) {
+ return admission.Warnings{}, nil
+}
diff --git a/internal/admission/mctx/validate.go b/internal/admission/mctx/validate.go
new file mode 100644
index 000000000..dd0f3c161
--- /dev/null
+++ b/internal/admission/mctx/validate.go
@@ -0,0 +1,76 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 mctx
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/errors"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s/dynamic"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+func validateCreate(ctx context.Context, obj runtime.Object) *errors.AdmissionErrors {
+ errs := errors.NewAdmissionErrors()
+
+ if context, ok := obj.(custom.ContextResource); ok {
+ errs.Add(validateSecretRef(ctx, context))
+ errs.Add(validateContextIsAvailable(ctx, context))
+ }
+
+ return errs
+}
+
+func validateSecretRef(ctx context.Context, context custom.ContextResource) *errors.AdmissionError {
+ if context.HasSecretRef() {
+ if err := dynamic.ExpectResolvedSecret(ctx, context.GetSecretRef(), context.GetNamespace()); err != nil {
+ return errors.NewSevere(
+ "secret [%v] doesn't exist in the cluster",
+ context.GetSecretRef(),
+ )
+ }
+ }
+ return nil
+}
+
+func validateContextIsAvailable(ctx context.Context, context custom.ContextResource) *errors.AdmissionError {
+ apim, err := apim.FromContext(ctx, context, context.GetNamespace())
+ if err != nil {
+ return errors.NewSevere(err.Error())
+ }
+ _, err = apim.Env.Get()
+
+ if errors.IsNetworkError(err) {
+ return errors.NewWarning(
+ "unable to reach APIM, [%s] is not available",
+ apim.Context.GetURL(),
+ )
+ }
+
+ if errors.IsUnauthorized(err) {
+ return errors.NewSevere(
+ "bad credentials for context [%s]",
+ context.GetName(),
+ )
+ }
+
+ if errors.IsBadRequest(err) {
+ return errors.NewSevere(err.Error())
+ }
+
+ return nil
+}
diff --git a/internal/admission/webhook/apiv4defenition.go b/internal/admission/webhook/apiv4defenition.go
deleted file mode 100644
index 92ccaa434..000000000
--- a/internal/admission/webhook/apiv4defenition.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
-//
-// 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 webhook
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/url"
- "strings"
-
- v4 "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/v4"
- "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/refs"
- "github.com/gravitee-io/gravitee-kubernetes-operator/internal/env"
- "github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
-)
-
-func ValidateApiV4(ctx context.Context, api *v4.Api, name, ns string,
- ctxRef *refs.NamespacedName) (admission.Warnings, error) {
- // make sure Management Context exist before creating the API Definition resource
- if ctxRef != nil { //nolint:nestif // nested if is needed
- gvr := schema.GroupVersionResource{
- Group: "gravitee.io",
- Version: "v1alpha1",
- Resource: "apiv4definitions",
- }
- if _, err := k8s.GetDynamicClient().
- Resource(gvr).
- Namespace(ctxRef.Namespace).
- Get(ctx, ctxRef.Name, metav1.GetOptions{}); err != nil {
- return admission.Warnings{}, fmt.Errorf("can't create API [%s] because it is using "+
- "management context [%v] that doesn't exist in the cluster", api.Name, ctxRef)
- }
- } else {
- // check for unique context path
- apis, err := getListOfExistingApis(ctx, ns)
-
- if err != nil {
- return admission.Warnings{}, fmt.Errorf("can't list existing APIs")
- }
-
- existingListeners := make([]*v4.GenericListener, 0)
- for _, item := range apis.Items {
- bytes, mErr := json.Marshal(item.Object["spec"])
- if mErr != nil {
- return admission.Warnings{}, mErr
- }
-
- ea := new(v4.Api)
- err = json.Unmarshal(bytes, ea)
- if err != nil {
- return admission.Warnings{}, err
- }
-
- if name != item.Object["metadata"].(map[string]interface{})["name"] ||
- ns != item.Object["metadata"].(map[string]interface{})["namespace"] {
- existingListeners = append(existingListeners, ea.Listeners...)
- }
- }
-
- if err = validateApiContextPath(existingListeners, api.Listeners); err != nil {
- return admission.Warnings{}, err
- }
- }
-
- return admission.Warnings{}, nil
-}
-
-func getListOfExistingApis(ctx context.Context, ns string) (*unstructured.UnstructuredList, error) {
- gvr := schema.GroupVersionResource{
- Group: "gravitee.io",
- Version: "v1alpha1",
- Resource: "apiv4definitions",
- }
- if !env.Config.CheckApiContextPathConflictInCluster {
- return k8s.GetDynamicClient().
- Resource(gvr).
- Namespace(ns).
- List(ctx, metav1.ListOptions{})
- } else {
- return k8s.GetDynamicClient().
- Resource(gvr).
- List(ctx, metav1.ListOptions{})
- }
-}
-
-func validateApiContextPath(existingListeners, listeners []*v4.GenericListener) error {
- apiPaths := make([]string, 0)
- for _, l := range listeners {
- for _, s := range parseListener(l) {
- p, err := url.Parse(s)
- if err != nil {
- return err
- }
- apiPaths = append(apiPaths, p.String())
- }
- }
-
- for _, l := range existingListeners {
- paths := parseListener(l)
- err := findDuplicatePath(paths, apiPaths)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func parseListener(l v4.Listener) []string {
- if l == nil {
- return []string{}
- }
-
- switch t := l.(type) {
- case *v4.GenericListener:
- return parseListener(t.ToListener())
- case *v4.HttpListener:
- {
- paths := make([]string, 0)
- for _, path := range t.Paths {
- p := fmt.Sprintf("%s/%s", path.Host, path.Path)
- paths = append(paths, strings.ReplaceAll(p, "//", "/"))
- }
- return paths
- }
- case *v4.TCPListener:
- return t.Hosts
- }
-
- return []string{}
-}
-
-func findDuplicatePath(existingPaths []string, newPaths []string) error {
- for _, ep := range existingPaths {
- for _, np := range newPaths {
- if ep == np {
- return fmt.Errorf("invalid API context path [%s]. Another API with the same path already exists", ep)
- }
- }
- }
- return nil
-}
diff --git a/internal/apim/apim.go b/internal/apim/apim.go
index feb0d756e..3a811509b 100644
--- a/internal/apim/apim.go
+++ b/internal/apim/apim.go
@@ -17,116 +17,57 @@ package apim
import (
"context"
- "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/management"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim/client"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim/service"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/http"
- "github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s/dynamic"
"github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
-
- coreV1 "k8s.io/api/core/v1"
- "sigs.k8s.io/controller-runtime/pkg/log"
-)
-
-const (
- bearerTokenSecretKey = "bearerToken"
- usernameSecretKey = "username"
- passwordSecretKey = "password"
)
// APIM wraps services needed to sync resources with a given environment on a Gravitee.io APIM instance.
type APIM struct {
APIs *service.APIs
Applications *service.Applications
+ Env *service.Env
- Context *management.Context
+ Context custom.Context
}
// EnvID returns the environment ID of the current managed APIM instance.
func (apim *APIM) EnvID() string {
- return apim.Context.EnvId
+ return apim.Context.GetEnvID()
}
// OrgID returns the organization ID of the current managed APIM instance.
func (apim *APIM) OrgID() string {
- return apim.Context.OrgId
+ return apim.Context.GetOrgID()
}
// FromContext returns a new APIM instance from a given reconcile context and management context.
-func FromContext(ctx context.Context, managementContext *management.Context) (*APIM, error) {
- orgID, envID := managementContext.OrgId, managementContext.EnvId
- urls, err := client.NewURLs(managementContext.BaseUrl, orgID, envID)
+func FromContext(ctx context.Context, context custom.Context, parentNs string) (*APIM, error) {
+ orgID, envID := context.GetOrgID(), context.GetEnvID()
+ urls, err := client.NewURLs(context.GetURL(), orgID, envID)
if err != nil {
return nil, err
}
client := &client.Client{
- HTTP: http.NewClient(ctx, toHttpAuth(managementContext)),
+ HTTP: http.NewClient(ctx, toHttpAuth(context)),
URLs: urls,
}
return &APIM{
APIs: service.NewAPIs(client),
Applications: service.NewApplications(client),
- Context: managementContext,
+ Env: service.NewEnv(client),
+ Context: context,
}, nil
}
-func FromContextRef(ctx context.Context, ref custom.ResourceRef) (*APIM, error) {
- managementContext, err := resolveContext(ctx, ref)
- if err != nil {
- return nil, err
- }
- return FromContext(ctx, managementContext)
-}
-
-func resolveContext(
- ctx context.Context,
- ref custom.ResourceRef,
-) (*management.Context, error) {
- log.FromContext(ctx).
- WithValues("namespace", ref.GetNamespace()).
- WithValues("name", ref.GetName()).
- Info("Resolving management context")
-
- context, err := k8s.ResolveContext(ctx, ref)
+func FromContextRef(ctx context.Context, ref custom.ResourceRef, parentNs string) (*APIM, error) {
+ context, err := dynamic.ResolveContext(ctx, ref, parentNs)
if err != nil {
return nil, err
}
-
- if err = resolveContextSecrets(ctx, context, ref); err != nil {
- return nil, err
- }
-
- return context, nil
-}
-
-func resolveContextSecrets(ctx context.Context, context *management.Context, ref custom.ResourceRef) error {
- if context.HasSecretRef() {
- secret := new(coreV1.Secret)
-
- secretKey := context.SecretRef().NamespacedName()
- secretKey.Namespace = getSecretNamespace(context, ref)
-
- if err := k8s.GetClient().Get(ctx, secretKey, secret); err != nil {
- return err
- }
-
- bearerToken := string(secret.Data[bearerTokenSecretKey])
- username := string(secret.Data[usernameSecretKey])
- password := string(secret.Data[passwordSecretKey])
-
- context.SetToken(bearerToken)
- context.SetCredentials(username, password)
- }
-
- return nil
-}
-
-func getSecretNamespace(context *management.Context, ref custom.ResourceRef) string {
- secretRef := context.SecretRef()
- if secretRef.Namespace != "" {
- return secretRef.Namespace
- }
- return ref.GetNamespace()
+ return FromContext(ctx, context, parentNs)
}
diff --git a/internal/apim/auth.go b/internal/apim/auth.go
index 5a4a7f044..025f95815 100644
--- a/internal/apim/auth.go
+++ b/internal/apim/auth.go
@@ -15,36 +15,36 @@
package apim
import (
- "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/management"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/http"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
)
-func toHttpAuth(management *management.Context) *http.Auth {
+func toHttpAuth(management custom.Context) *http.Auth {
if !management.HasAuthentication() {
return nil
}
return &http.Auth{
- Basic: toBasicAuth(management.Auth),
- Token: toBearer(management.Auth),
+ Basic: toBasicAuth(management.GetAuth()),
+ Token: toBearer(management.GetAuth()),
}
}
-func toBasicAuth(auth *management.Auth) *http.BasicAuth {
- if auth == nil || auth.Credentials == nil {
+func toBasicAuth(auth custom.Auth) *http.BasicAuth {
+ if auth == nil || !auth.HasCredentials() {
return nil
}
return &http.BasicAuth{
- Username: auth.Credentials.Username,
- Password: auth.Credentials.Password,
+ Username: auth.GetCredentials().GetUsername(),
+ Password: auth.GetCredentials().GetPassword(),
}
}
-func toBearer(auth *management.Auth) http.BearerToken {
+func toBearer(auth custom.Auth) http.BearerToken {
if auth == nil {
return ""
}
- return http.BearerToken(auth.BearerToken)
+ return http.BearerToken(auth.GetBearerToken())
}
diff --git a/internal/apim/model/api.go b/internal/apim/model/api.go
index e06497929..cb2a4409f 100644
--- a/internal/apim/model/api.go
+++ b/internal/apim/model/api.go
@@ -54,7 +54,7 @@ func NewKubernetesContext() *DefinitionContext {
}
type ApiListItem struct {
- Id string `json:"id"`
+ ID string `json:"id"`
Name string `json:"name"`
State string `json:"state"`
Visibility string `json:"visibility"`
@@ -80,8 +80,8 @@ func ApiStateToAction(s base.ApiState) Action {
}
type Plan struct {
- Id string `json:"id"`
- CrossId string `json:"crossId"`
+ ID string `json:"id"`
+ CrossID string `json:"crossId"`
Name string `json:"name"`
Security PlanSecurityType `json:"security"`
Status PlanStatus `json:"status"`
diff --git a/internal/apim/model/api_key.go b/internal/apim/model/api_key.go
index d6649ba92..b424bd349 100644
--- a/internal/apim/model/api_key.go
+++ b/internal/apim/model/api_key.go
@@ -15,6 +15,6 @@
package model
type ApiKeyEntity struct {
- Id string `json:"id"`
+ ID string `json:"id"`
Key string `json:"key"`
}
diff --git a/internal/apim/model/application.go b/internal/apim/model/application.go
index 585bc30a4..9668d56b1 100644
--- a/internal/apim/model/application.go
+++ b/internal/apim/model/application.go
@@ -19,7 +19,7 @@ import (
)
type Application struct {
- Id string `json:"id,omitempty"`
+ ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Status string `json:"status,omitempty"`
Description string `json:"description,omitempty"`
@@ -34,7 +34,7 @@ type Application struct {
type ApplicationMetaData struct {
Name string `json:"name"`
- ApplicationId string `json:"applicationId,omitempty"`
+ ApplicationID string `json:"applicationId,omitempty"`
Value string `json:"value,omitempty"`
DefaultValue string `json:"defaultValue,omitempty"`
Format *application.MetaDataFormat `json:"format,omitempty"`
diff --git a/pkg/types/list/list.go b/internal/apim/model/env.go
similarity index 76%
rename from pkg/types/list/list.go
rename to internal/apim/model/env.go
index c6a087eeb..77f600319 100644
--- a/pkg/types/list/list.go
+++ b/internal/apim/model/env.go
@@ -12,16 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package list
+package model
-import "sigs.k8s.io/controller-runtime/pkg/client"
-
-type Item interface {
- GetName() string
- GetNamespace() string
-}
-
-type Interface interface {
- client.ObjectList
- GetItems() []Item
+type Env struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
}
diff --git a/internal/apim/model/subscription.go b/internal/apim/model/subscription.go
index e9b35b5c4..ed8ca5919 100644
--- a/internal/apim/model/subscription.go
+++ b/internal/apim/model/subscription.go
@@ -15,5 +15,5 @@
package model
type Subscription struct {
- Id string `json:"id"`
+ ID string `json:"id"`
}
diff --git a/internal/apim/service/apis.go b/internal/apim/service/apis.go
index 8ed5a660d..a828a7741 100644
--- a/internal/apim/service/apis.go
+++ b/internal/apim/service/apis.go
@@ -16,6 +16,7 @@ package service
import (
"net/http"
+ "strconv"
v4 "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/v4"
@@ -103,7 +104,15 @@ func (svc *APIs) ImportV2(method string, spec *v2.Api) (*model.ApiEntity, error)
}
func (svc *APIs) ImportV4(spec *v4.Api) (*v4.Status, error) {
- url := svc.EnvV2Target("apis/_import/crd")
+ return svc.importV4(spec, false)
+}
+
+func (svc *APIs) DryRunImportV4(spec *v4.Api) (*v4.Status, error) {
+ return svc.importV4(spec, true)
+}
+
+func (svc *APIs) importV4(spec *v4.Api, dryRun bool) (*v4.Status, error) {
+ url := svc.EnvV2Target("apis/_import/crd").WithQueryParam("dryRun", strconv.FormatBool(dryRun))
status := new(v4.Status)
if err := svc.HTTP.Put(url.String(), spec, status); err != nil {
diff --git a/internal/apim/service/applications.go b/internal/apim/service/applications.go
index 31ed669b4..9949b439d 100644
--- a/internal/apim/service/applications.go
+++ b/internal/apim/service/applications.go
@@ -51,12 +51,12 @@ func (svc *Applications) Search(query string, status string) ([]model.Applicatio
return *applications, nil
}
-func (svc *Applications) GetByID(appId string) (*model.Application, error) {
- if appId == "" {
+func (svc *Applications) GetByID(appID string) (*model.Application, error) {
+ if appID == "" {
return nil, errors.NewNotFoundError()
}
- url := svc.EnvV1Target(applicationsPath).WithPath(appId)
+ url := svc.EnvV1Target(applicationsPath).WithPath(appID)
application := new(model.Application)
if err := svc.HTTP.Get(url.String(), application); err != nil {
@@ -66,12 +66,12 @@ func (svc *Applications) GetByID(appId string) (*model.Application, error) {
return application, nil
}
-func (svc *Applications) GetMetadataByApplicationID(appId string) (*[]model.ApplicationMetaData, error) {
- if appId == "" {
+func (svc *Applications) GetMetadataByApplicationID(appID string) (*[]model.ApplicationMetaData, error) {
+ if appID == "" {
return nil, fmt.Errorf("can't retrieve metadata without application id")
}
- url := svc.EnvV1Target(applicationsPath).WithPath(appId).WithPath(metadataPath)
+ url := svc.EnvV1Target(applicationsPath).WithPath(appID).WithPath(metadataPath)
application := new([]model.ApplicationMetaData)
if err := svc.HTTP.Get(url.String(), application); err != nil {
@@ -92,14 +92,14 @@ func (svc *Applications) CreateOrUpdate(spec *application.Application) (*applica
return status, nil
}
-func (svc *Applications) Delete(appId string) error {
- url := svc.EnvV1Target(applicationsPath).WithPath(appId)
+func (svc *Applications) Delete(appID string) error {
+ url := svc.EnvV1Target(applicationsPath).WithPath(appID)
return svc.HTTP.Delete(url.String(), nil)
}
-func (svc *Applications) CreateOrUpdateMetadata(method string, appId string,
+func (svc *Applications) CreateOrUpdateMetadata(method string, appID string,
spec any, key string) (*model.ApplicationMetaData, error) {
- url := svc.EnvV1Target(applicationsPath).WithPath(appId).WithPath(metadataPath)
+ url := svc.EnvV1Target(applicationsPath).WithPath(appID).WithPath(metadataPath)
fun := svc.HTTP.Post
if method == http.MethodPut {
url = url.WithPath(key)
@@ -114,7 +114,7 @@ func (svc *Applications) CreateOrUpdateMetadata(method string, appId string,
return md, nil
}
-func (svc *Applications) DeleteMetadata(appId string, key string) error {
- url := svc.EnvV1Target(applicationsPath).WithPath(appId).WithPath(metadataPath).WithPath(key)
+func (svc *Applications) DeleteMetadata(appID string, key string) error {
+ url := svc.EnvV1Target(applicationsPath).WithPath(appID).WithPath(metadataPath).WithPath(key)
return svc.HTTP.Delete(url.String(), nil)
}
diff --git a/internal/apim/service/env.go b/internal/apim/service/env.go
index 3b99280d2..c066fd500 100644
--- a/internal/apim/service/env.go
+++ b/internal/apim/service/env.go
@@ -31,3 +31,11 @@ func (svc *Env) CreateGroup(group *model.Group) error {
url := svc.EnvV1Target("configuration").WithPath("groups")
return svc.HTTP.Post(url.String(), group, group)
}
+
+func (svc *Env) Get() (*model.Env, error) {
+ env := new(model.Env)
+ if err := svc.HTTP.Get(svc.URLs.EnvV2.String(), env); err != nil {
+ return nil, err
+ }
+ return env, nil
+}
diff --git a/internal/errors/admission.go b/internal/errors/admission.go
new file mode 100644
index 000000000..61d50d165
--- /dev/null
+++ b/internal/errors/admission.go
@@ -0,0 +1,107 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 errors
+
+import (
+ "fmt"
+
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+type Severity string
+
+const (
+ Severe = Severity("severe")
+ Warning = Severity("warning")
+)
+
+type AdmissionError struct {
+ Severity Severity
+ Message string
+}
+
+type AdmissionErrors struct {
+ Warning []*AdmissionError
+ Severe []*AdmissionError
+}
+
+func NewAdmissionErrors() *AdmissionErrors {
+ return &AdmissionErrors{
+ Severe: make([]*AdmissionError, 0),
+ Warning: make([]*AdmissionError, 0),
+ }
+}
+
+func (errs *AdmissionErrors) Map() (admission.Warnings, error) {
+ warnings := admission.Warnings{}
+ for _, w := range errs.Warning {
+ warnings = append(warnings, w.Error())
+ }
+ if len(errs.Severe) == 0 {
+ return warnings, nil
+ }
+ return warnings, errs.Severe[0]
+}
+
+func (errs *AdmissionErrors) MergeWith(other *AdmissionErrors) *AdmissionErrors {
+ if other == nil {
+ return errs
+ }
+ errs.Warning = append(errs.Warning, other.Warning...)
+ errs.Severe = append(errs.Severe, other.Severe...)
+ return errs
+}
+
+func (errs *AdmissionErrors) IsSevere() bool {
+ return len(errs.Severe) > 0
+}
+
+func (errs *AdmissionErrors) AddSevere(format string, args ...any) {
+ errs.Severe = append(errs.Severe, NewSevere(format, args...))
+}
+
+func (errs *AdmissionErrors) AddWarning(format string, args ...any) {
+ errs.Warning = append(errs.Warning, NewWarning(format, args...))
+}
+
+func (errs *AdmissionErrors) Add(err *AdmissionError) {
+ if err == nil {
+ return
+ }
+ if err.Severity == Severe {
+ errs.Severe = append(errs.Severe, err)
+ }
+ if err.Severity == Warning {
+ errs.Warning = append(errs.Warning, err)
+ }
+}
+
+func (err *AdmissionError) Error() string {
+ return err.Message
+}
+
+func NewSevere(format string, args ...any) *AdmissionError {
+ return &AdmissionError{
+ Severity: Severe,
+ Message: fmt.Sprintf(format, args...),
+ }
+}
+
+func NewWarning(format string, args ...any) *AdmissionError {
+ return &AdmissionError{
+ Severity: Warning,
+ Message: fmt.Sprintf(format, args...),
+ }
+}
diff --git a/internal/errors/errors.go b/internal/errors/errors.go
index 418dfc726..d83a2c3be 100644
--- a/internal/errors/errors.go
+++ b/internal/errors/errors.go
@@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"io"
+ "net"
"net/http"
kErrors "k8s.io/apimachinery/pkg/util/errors"
@@ -129,6 +130,19 @@ func IsNotFound(err error) bool {
return false
}
+func IsBadRequest(err error) bool {
+ serverError := &ServerError{}
+ if errors.As(err, serverError) {
+ return serverError.StatusCode == http.StatusBadRequest
+ }
+ return false
+}
+
+func IsNetworkError(err error) bool {
+ opErr := new(net.OpError)
+ return errors.As(err, &opErr)
+}
+
func IsUnauthorized(err error) bool {
serverError := &ServerError{}
if errors.As(err, serverError) {
diff --git a/internal/indexer/indexer.go b/internal/indexer/indexer.go
index 4f7047cad..6b5a5737e 100644
--- a/internal/indexer/indexer.go
+++ b/internal/indexer/indexer.go
@@ -17,7 +17,7 @@ package indexer
import (
"context"
- gio "github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s"
"github.com/gravitee-io/gravitee-kubernetes-operator/pkg/keys"
v1 "k8s.io/api/networking/v1"
@@ -54,29 +54,34 @@ func InitCache(ctx context.Context, cache cache.Cache) error {
errs := make([]error, 0)
contextIndexer := newIndexer(ApiContextField, indexManagementContexts)
- if err := cache.IndexField(ctx, &gio.ApiDefinition{}, contextIndexer.Field, contextIndexer.Func); err != nil {
+ if err := cache.IndexField(ctx, &v1alpha1.ApiDefinition{}, contextIndexer.Field, contextIndexer.Func); err != nil {
errs = append(errs, err)
}
apiV4ContextIndexer := newIndexer(ApiV4ContextField, indexApiV4ManagementContexts)
- if err := cache.IndexField(ctx, &gio.ApiV4Definition{}, apiV4ContextIndexer.Field,
+ if err := cache.IndexField(ctx, &v1alpha1.ApiV4Definition{}, apiV4ContextIndexer.Field,
apiV4ContextIndexer.Func); err != nil {
errs = append(errs, err)
}
resourceIndexer := newIndexer(ApiResourceField, indexApiResourceRefs)
- if err := cache.IndexField(ctx, &gio.ApiDefinition{}, resourceIndexer.Field, resourceIndexer.Func); err != nil {
+ if err := cache.IndexField(ctx, &v1alpha1.ApiDefinition{}, resourceIndexer.Field, resourceIndexer.Func); err != nil {
errs = append(errs, err)
}
apiV4ResourceIndexer := newIndexer(ApiV4ResourceField, indexIApiV4ResourceRefs)
- if err := cache.IndexField(ctx, &gio.ApiV4Definition{}, apiV4ResourceIndexer.Field,
+ if err := cache.IndexField(ctx, &v1alpha1.ApiV4Definition{}, apiV4ResourceIndexer.Field,
apiV4ResourceIndexer.Func); err != nil {
errs = append(errs, err)
}
secretRefIndexer := newIndexer(SecretRefField, indexManagementContextSecrets)
- if err := cache.IndexField(ctx, &gio.ManagementContext{}, secretRefIndexer.Field, secretRefIndexer.Func); err != nil {
+ if err := cache.IndexField(
+ ctx,
+ &v1alpha1.ManagementContext{},
+ secretRefIndexer.Field,
+ secretRefIndexer.Func,
+ ); err != nil {
errs = append(errs, err)
}
@@ -91,7 +96,7 @@ func InitCache(ctx context.Context, cache cache.Cache) error {
}
appContextIndexer := newIndexer(AppContextField, indexApplicationManagementContexts)
- if err := cache.IndexField(ctx, &gio.Application{}, appContextIndexer.Field, appContextIndexer.Func); err != nil {
+ if err := cache.IndexField(ctx, &v1alpha1.Application{}, appContextIndexer.Field, appContextIndexer.Func); err != nil {
errs = append(errs, err)
}
@@ -120,7 +125,7 @@ func newIndexer[T client.Object](field IndexField, doIndex func(T, *[]string)) I
}
}
-func indexManagementContexts(api *gio.ApiDefinition, fields *[]string) {
+func indexManagementContexts(api *v1alpha1.ApiDefinition, fields *[]string) {
if api.Spec.Context == nil {
return
}
@@ -128,7 +133,7 @@ func indexManagementContexts(api *gio.ApiDefinition, fields *[]string) {
*fields = append(*fields, api.Spec.Context.String())
}
-func indexApiV4ManagementContexts(api *gio.ApiV4Definition, fields *[]string) {
+func indexApiV4ManagementContexts(api *v1alpha1.ApiV4Definition, fields *[]string) {
if api.Spec.Context == nil {
return
}
@@ -136,13 +141,13 @@ func indexApiV4ManagementContexts(api *gio.ApiV4Definition, fields *[]string) {
*fields = append(*fields, api.Spec.Context.String())
}
-func indexManagementContextSecrets(context *gio.ManagementContext, fields *[]string) {
+func indexManagementContextSecrets(context *v1alpha1.ManagementContext, fields *[]string) {
if context.Spec.HasSecretRef() {
*fields = append(*fields, context.Spec.SecretRef().String())
}
}
-func indexApiResourceRefs(api *gio.ApiDefinition, fields *[]string) {
+func indexApiResourceRefs(api *v1alpha1.ApiDefinition, fields *[]string) {
if api.Spec.Resources == nil {
return
}
@@ -154,7 +159,7 @@ func indexApiResourceRefs(api *gio.ApiDefinition, fields *[]string) {
}
}
-func indexIApiV4ResourceRefs(api *gio.ApiV4Definition, fields *[]string) {
+func indexIApiV4ResourceRefs(api *v1alpha1.ApiV4Definition, fields *[]string) {
if api.Spec.Resources == nil {
return
}
@@ -188,7 +193,7 @@ func indexTLSSecret(ing *v1.Ingress, fields *[]string) {
}
}
-func indexApplicationManagementContexts(application *gio.Application, fields *[]string) {
+func indexApplicationManagementContexts(application *v1alpha1.Application, fields *[]string) {
if application.Spec.Context == nil {
return
}
diff --git a/internal/k8s/client.go b/internal/k8s/client.go
index 7130919be..0648a9920 100644
--- a/internal/k8s/client.go
+++ b/internal/k8s/client.go
@@ -15,29 +15,15 @@
package k8s
import (
- "sync"
-
- "k8s.io/client-go/dynamic"
"sigs.k8s.io/controller-runtime/pkg/client"
-
- ctrl "sigs.k8s.io/controller-runtime"
)
var cli client.Client
-var dynamicClient *dynamic.DynamicClient
-var once sync.Once
func RegisterClient(c client.Client) {
- once.Do(func() {
- cli = c
- dynamicClient = dynamic.NewForConfigOrDie(ctrl.GetConfigOrDie())
- })
+ cli = c
}
func GetClient() client.Client {
return cli
}
-
-func GetDynamicClient() *dynamic.DynamicClient {
- return dynamicClient
-}
diff --git a/internal/admission/common/mutate/context.go b/internal/k8s/dynamic/client.go
similarity index 66%
rename from internal/admission/common/mutate/context.go
rename to internal/k8s/dynamic/client.go
index 5f7312680..dec751eab 100644
--- a/internal/admission/common/mutate/context.go
+++ b/internal/k8s/dynamic/client.go
@@ -12,12 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package mutate
+package dynamic
-import "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
+import (
+ "sync"
-func SetDefaults(ctxAware custom.ContextAwareResource) {
- if ctxAware.HasContext() && ctxAware.ContextRef().IsMissingNamespace() {
- ctxAware.ContextRef().SetNamespace(ctxAware.GetNamespace())
- }
+ "k8s.io/client-go/dynamic"
+
+ ctrl "sigs.k8s.io/controller-runtime"
+)
+
+var dynamicClient *dynamic.DynamicClient
+var once sync.Once
+
+func GetClient() *dynamic.DynamicClient {
+ once.Do(func() {
+ dynamicClient = dynamic.NewForConfigOrDie(ctrl.GetConfigOrDie())
+ })
+ return dynamicClient
}
diff --git a/internal/k8s/dynamic/convert.go b/internal/k8s/dynamic/convert.go
new file mode 100644
index 000000000..2edbc0f78
--- /dev/null
+++ b/internal/k8s/dynamic/convert.go
@@ -0,0 +1,47 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 dynamic
+
+import (
+ "encoding/json"
+
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+)
+
+func Convert[T any](source any, target T) (T, error) {
+ b, err := json.Marshal(source)
+ if err != nil {
+ return target, err
+ }
+
+ if err = json.Unmarshal(b, target); err != nil {
+ return target, err
+ }
+
+ return target, nil
+}
+
+func ConvertList[T any](list *unstructured.UnstructuredList) ([]T, error) {
+ apis := make([]T, 0)
+ t := new(T)
+ for _, it := range list.Items {
+ api, err := Convert(it.Object["spec"], *t)
+ if err != nil {
+ return apis, err
+ }
+ apis = append(apis, api)
+ }
+ return apis, nil
+}
diff --git a/internal/k8s/dynamic/mctx.go b/internal/k8s/dynamic/mctx.go
new file mode 100644
index 000000000..f2a4b0ee8
--- /dev/null
+++ b/internal/k8s/dynamic/mctx.go
@@ -0,0 +1,66 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 dynamic
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/management"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/keys"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+const (
+ bearerTokenSecretKey = "bearerToken"
+ usernameSecretKey = "username"
+ passwordSecretKey = "password"
+)
+
+func ExpectResolvedContext(ctx context.Context, ref custom.ResourceRef, parentNs string) error {
+ if _, err := ResolveContext(ctx, ref, parentNs); err != nil {
+ return err
+ }
+ return nil
+}
+
+func ResolveContext(ctx context.Context, ref custom.ResourceRef, parentNs string) (*management.Context, error) {
+ context, err := resolveRefSpec(ctx, ref, parentNs, schema.GroupVersionResource{
+ Group: keys.CrdGroup,
+ Version: keys.CrdVersion,
+ Resource: "managementcontexts",
+ }, new(management.Context))
+ if err != nil {
+ return nil, err
+ }
+
+ return injectSecretIfAny(ctx, context, parentNs)
+}
+
+func injectSecretIfAny(ctx context.Context, mCtx *management.Context, parentNs string) (*management.Context, error) {
+ if mCtx.HasSecretRef() {
+ secret, err := ResolveSecret(ctx, mCtx.SecretRef(), parentNs)
+ if err != nil {
+ return nil, err
+ }
+ bearerToken := string(secret.Data[bearerTokenSecretKey])
+ username := string(secret.Data[usernameSecretKey])
+ password := string(secret.Data[passwordSecretKey])
+
+ mCtx.SetToken(bearerToken)
+ mCtx.SetCredentials(username, password)
+ }
+ return mCtx, nil
+}
diff --git a/internal/k8s/ref.go b/internal/k8s/dynamic/resolve.go
similarity index 55%
rename from internal/k8s/ref.go
rename to internal/k8s/dynamic/resolve.go
index efa8b644b..1c9d8eeec 100644
--- a/internal/k8s/ref.go
+++ b/internal/k8s/dynamic/resolve.go
@@ -12,61 +12,57 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package k8s
+package dynamic
import (
"context"
- "encoding/json"
- "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/management"
- "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/keys"
"github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
- coreV1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)
-func ResolveContext(ctx context.Context, ref custom.ResourceRef) (*management.Context, error) {
- return resolveRef(ctx, ref, schema.GroupVersionResource{
- Group: keys.CrdGroup,
- Version: keys.CrdVersion,
- Resource: "managementcontexts",
- }, new(management.Context))
-}
-
-func ResolveSecret(ctx context.Context, ref custom.ResourceRef) (*coreV1.Secret, error) {
- return resolveRef(ctx, ref, schema.GroupVersionResource{
- Group: "",
- Version: "v1",
- Resource: "secrets",
- }, new(coreV1.Secret))
-}
-
-func resolveRef[T any](
+func resolveRefSpec[T any](
ctx context.Context,
ref custom.ResourceRef,
+ parentNs string,
gvr schema.GroupVersionResource,
target T,
) (T, error) {
- dynamic, err := GetDynamicClient().
- Resource(gvr).
- Namespace(ref.GetNamespace()).
- Get(ctx, ref.GetName(), v1.GetOptions{})
-
+ dynamic, err := resolveDynamic(ctx, ref, parentNs, gvr)
if err != nil {
return target, err
}
+ return Convert(dynamic.Object["spec"], target)
+}
- spec := dynamic.Object["spec"]
-
- b, err := json.Marshal(spec)
+func resolveRef[T any](
+ ctx context.Context,
+ ref custom.ResourceRef,
+ parentNs string,
+ gvr schema.GroupVersionResource,
+ target T,
+) (T, error) {
+ dynamic, err := resolveDynamic(ctx, ref, parentNs, gvr)
if err != nil {
return target, err
}
+ return Convert(dynamic.Object, target)
+}
- if err = json.Unmarshal(b, target); err != nil {
- return target, err
+func resolveDynamic(
+ ctx context.Context,
+ ref custom.ResourceRef,
+ parentNs string,
+ gvr schema.GroupVersionResource,
+) (*unstructured.Unstructured, error) {
+ if ref.GetNamespace() == "" {
+ ref.SetNamespace(parentNs)
}
- return target, nil
+ return GetClient().
+ Resource(gvr).
+ Namespace(ref.GetNamespace()).
+ Get(ctx, ref.GetName(), v1.GetOptions{})
}
diff --git a/internal/k8s/dynamic/secret.go b/internal/k8s/dynamic/secret.go
new file mode 100644
index 000000000..8d697074f
--- /dev/null
+++ b/internal/k8s/dynamic/secret.go
@@ -0,0 +1,39 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 dynamic
+
+import (
+ "context"
+
+ coreV1 "k8s.io/api/core/v1"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/pkg/types/k8s/custom"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+func ExpectResolvedSecret(ctx context.Context, ref custom.ResourceRef, parentNs string) error {
+ if _, err := ResolveSecret(ctx, ref, parentNs); err != nil {
+ return err
+ }
+ return nil
+}
+
+func ResolveSecret(ctx context.Context, ref custom.ResourceRef, parentNs string) (*coreV1.Secret, error) {
+ return resolveRef(ctx, ref, parentNs, schema.GroupVersionResource{
+ Group: "",
+ Version: "v1",
+ Resource: "secrets",
+ }, new(coreV1.Secret))
+}
diff --git a/internal/template/resolver.go b/internal/template/resolver.go
index 90bd6d3a3..72bd7245f 100644
--- a/internal/template/resolver.go
+++ b/internal/template/resolver.go
@@ -22,13 +22,13 @@ import (
"strings"
"text/template"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s"
"github.com/gravitee-io/gravitee-kubernetes-operator/pkg/keys"
netv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
util "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
- "github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
"gopkg.in/yaml.v3"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
diff --git a/main.go b/main.go
index 771fc42dc..1144fef25 100644
--- a/main.go
+++ b/main.go
@@ -26,6 +26,10 @@ import (
"os"
"strings"
+ v2Admission "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/v2"
+ v4Admission "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/v4"
+ appAdmission "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/application"
+ mctxAdmission "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/mctx"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/k8s"
wk "github.com/gravitee-io/gravitee-kubernetes-operator/internal/webhook"
"gopkg.in/yaml.v3"
@@ -333,16 +337,16 @@ func setupAdmissionWebhooks(mgr manager.Manager) error {
if err := (&v1alpha1.ApiResource{}).SetupWebhookWithManager(mgr); err != nil {
return err
}
- if err := (&v1alpha1.ApiDefinition{}).SetupWebhookWithManager(mgr); err != nil {
+ if err := (v2Admission.AdmissionCtrl{}).SetupWithManager(mgr); err != nil {
return err
}
- if err := (&v1alpha1.ApiV4Definition{}).SetupWebhookWithManager(mgr); err != nil {
+ if err := (v4Admission.AdmissionCtrl{}).SetupWithManager(mgr); err != nil {
return err
}
- if err := (&v1alpha1.Application{}).SetupWebhookWithManager(mgr); err != nil {
+ if err := (appAdmission.AdmissionCtrl{}).SetupWithManager(mgr); err != nil {
return err
}
- if err := (&v1alpha1.ManagementContext{}).SetupWebhookWithManager(mgr); err != nil {
+ if err := (mctxAdmission.AdmissionCtrl{}).SetupWithManager(mgr); err != nil {
return err
}
diff --git a/pkg/types/k8s/custom/custom.go b/pkg/types/k8s/custom/custom.go
index 09018096f..1bbc41b87 100644
--- a/pkg/types/k8s/custom/custom.go
+++ b/pkg/types/k8s/custom/custom.go
@@ -45,10 +45,21 @@ type Resource interface {
// +k8s:deepcopy-gen=false
type ApiDefinition interface {
+ GetDefinitionVersion() ApiDefinitionVersion
+ GetContextPaths() ([]string, error)
+}
+
+// +k8s:deepcopy-gen=false
+type ApiDefinitionResource interface {
+ ContextAwareResource
+ ApiDefinition
+ GetDefinition() ApiDefinition
+ PopulateIDs(context Context)
+}
+
+// +k8s:deepcopy-gen=false
+type ApplicationResource interface {
ContextAwareResource
- Version() ApiDefinitionVersion
- OrgID() string
- EnvID() string
}
// +k8s:deepcopy-gen=false
@@ -69,7 +80,47 @@ type ContextAwareResource interface {
Resource
ContextRef() ResourceRef
HasContext() bool
- ID() string
+ GetID() string
+ GetOrgID() string
+ GetEnvID() string
+}
+
+// +k8s:deepcopy-gen=false
+type SecretAware interface {
+ GetSecretRef() ResourceRef
+ HasSecretRef() bool
+}
+
+// +k8s:deepcopy-gen=false
+type Context interface {
+ SecretAware
+ GetURL() string
+ GetEnvID() string
+ GetOrgID() string
+ HasAuthentication() bool
+ GetAuth() Auth
+}
+
+// +k8s:deepcopy-gen=false
+type ContextResource interface {
+ Context
+ Resource
+}
+
+// +k8s:deepcopy-gen=false
+type Auth interface {
+ GetBearerToken() string
+ HasCredentials() bool
+ GetCredentials() BasicAuth
+ GetSecretRef() ResourceRef
+ SetCredentials(username, password string)
+ SetToken(token string)
+}
+
+// +k8s:deepcopy-gen=false
+type BasicAuth interface {
+ GetUsername() string
+ GetPassword() string
}
// +k8s:deepcopy-gen=false
diff --git a/test/integration/admission/apiV2_create_withContext_andUnknownContext_test.go b/test/integration/admission/apiV2_create_withContext_andUnknownContext_test.go
new file mode 100644
index 000000000..db0f08cdf
--- /dev/null
+++ b/test/integration/admission/apiV2_create_withContext_andUnknownContext_test.go
@@ -0,0 +1,56 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 admission
+
+import (
+ "context"
+ "fmt"
+
+ v2 "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/v2"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/assert"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Validate create", labels.WithContext, func() {
+ interval := constants.Interval
+ ctx := context.Background()
+ admissionCtrl := v2.AdmissionCtrl{}
+
+ It("should return error on api creation with missing management context", func() {
+ fixtures := fixture.
+ Builder().
+ WithAPI(constants.ApiWithContextFile).
+ Build().
+ Apply()
+
+ By("checking that application does not pass validation")
+
+ Consistently(func() error {
+ _, err := admissionCtrl.ValidateCreate(ctx, fixtures.API)
+ return assert.Equals(
+ "error",
+ fmt.Errorf(
+ "resource [%s] references management context [default/dev-ctx] that doesn't exist in the cluster",
+ fixtures.API.Name,
+ ),
+ err,
+ )
+ }, constants.ConsistentTimeout, interval).ShouldNot(Succeed())
+ })
+})
diff --git a/test/integration/admissionwebhook/api_v4_create_withContext_missing_context_webhook_test.go b/test/integration/admission/apiV4_create_withContext_andUnknownContext_test.go
similarity index 78%
rename from test/integration/admissionwebhook/api_v4_create_withContext_missing_context_webhook_test.go
rename to test/integration/admission/apiV4_create_withContext_andUnknownContext_test.go
index 04696f6ff..9e14008b2 100644
--- a/test/integration/admissionwebhook/api_v4_create_withContext_missing_context_webhook_test.go
+++ b/test/integration/admission/apiV4_create_withContext_andUnknownContext_test.go
@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
"context"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ v4 "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/v4"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
@@ -27,20 +28,20 @@ import (
"k8s.io/apimachinery/pkg/types"
)
-var _ = Describe("Webhook", labels.WithContext, func() {
- timeout := constants.EventualTimeout / 10
+var _ = Describe("Validate create", labels.WithContext, func() {
interval := constants.Interval
-
ctx := context.Background()
+ admissionCtrl := v4.AdmissionCtrl{}
- It("should get errors for API creation, missing management context", func() {
+ It("should return error on API creation with missing management context", func() {
fixtures := fixture.
Builder().
WithAPIv4(constants.ApiV4WithContextFile).
Build().
Apply()
- By("Check API creation validation")
+ By("checking that validation fails")
+
Consistently(func() error {
api := new(v1alpha1.ApiV4Definition)
if err := manager.Client().Get(ctx, types.NamespacedName{
@@ -50,8 +51,8 @@ var _ = Describe("Webhook", labels.WithContext, func() {
return err
}
- _, err := api.ValidateCreate()
+ _, err := admissionCtrl.ValidateUpdate(ctx, api, api)
return err
- }, timeout, interval).ShouldNot(Succeed())
+ }, constants.ConsistentTimeout, interval).ShouldNot(Succeed())
})
})
diff --git a/test/integration/admissionwebhook/api_v4_create_withoutContext_invalid_path_webhook_test.go b/test/integration/admission/apiV4_create_withoutContext_andConflictingPath_test.go
similarity index 78%
rename from test/integration/admissionwebhook/api_v4_create_withoutContext_invalid_path_webhook_test.go
rename to test/integration/admission/apiV4_create_withoutContext_andConflictingPath_test.go
index 0d87b2194..6725d9c6d 100644
--- a/test/integration/admissionwebhook/api_v4_create_withoutContext_invalid_path_webhook_test.go
+++ b/test/integration/admission/apiV4_create_withoutContext_andConflictingPath_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
"context"
@@ -20,6 +20,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ v4 "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/v4"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
@@ -28,20 +29,20 @@ import (
. "github.com/onsi/gomega"
)
-var _ = Describe("Webhook", labels.WithContext, func() {
- timeout := constants.EventualTimeout / 10
+var _ = Describe("Validate create", labels.WithoutContext, func() {
interval := constants.Interval
-
ctx := context.Background()
+ admissionCtrl := v4.AdmissionCtrl{}
- It("should get errors for API creation because of existing path", func() {
+ It("should return error on API creation with conflicting path", func() {
fixtures := fixture.
Builder().
WithAPIv4(constants.ApiV4).
Build().
Apply()
- By("Check API creation validation")
+ By("checking that API creation does not pass validation")
+
Eventually(func() error {
api := &v1alpha1.ApiV4Definition{
ObjectMeta: metav1.ObjectMeta{
@@ -56,8 +57,8 @@ var _ = Describe("Webhook", labels.WithContext, func() {
return err
}
- _, err := api.ValidateCreate()
+ _, err := admissionCtrl.ValidateCreate(ctx, api)
return err
- }, timeout, interval).ShouldNot(Succeed())
+ }, constants.EventualTimeout, interval).ShouldNot(Succeed())
})
})
diff --git a/test/integration/admission/apiV4_update_withContext_andUnknownCategory_test.go b/test/integration/admission/apiV4_update_withContext_andUnknownCategory_test.go
new file mode 100644
index 000000000..ffdf94407
--- /dev/null
+++ b/test/integration/admission/apiV4_update_withContext_andUnknownCategory_test.go
@@ -0,0 +1,73 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 admission
+
+import (
+ "context"
+
+ apiV4 "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/v4"
+ v4 "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/v4"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/errors"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/assert"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/random"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Validate update", labels.WithContext, func() {
+ interval := constants.Interval
+ ctx := context.Background()
+ admissionCtrl := v4.AdmissionCtrl{}
+
+ It("should return warning on API creation with unknown category", func() {
+ fixtures := fixture.
+ Builder().
+ WithAPIv4(constants.ApiV4).
+ WithContext(constants.ContextWithCredentialsFile).
+ Build().
+ Apply()
+
+ By("preparing API for import")
+
+ fixtures.APIv4.Spec.DefinitionContext = apiV4.NewDefaultKubernetesContext()
+ fixtures.APIv4.PopulateIDs(fixtures.Context)
+
+ By("adding an unknown category to the API")
+
+ unknownCategory := random.GetName()
+
+ fixtures.APIv4.Spec.Categories = []string{unknownCategory}
+
+ By("checking that API validation returns warnings")
+
+ Eventually(func() error {
+ warnings, err := admissionCtrl.ValidateUpdate(ctx, fixtures.APIv4, fixtures.APIv4)
+ if err != nil {
+ return err
+ }
+ if err = assert.SliceOfSize("warnings", warnings, 1); err != nil {
+ return err
+ }
+ return assert.Equals(
+ "warning",
+ errors.NewWarning("category [%s] is not defined in environment [DEFAULT]", unknownCategory).Error(),
+ warnings[0],
+ )
+ }, constants.EventualTimeout, interval).Should(Succeed())
+ })
+})
diff --git a/test/integration/admissionwebhook/api_v4_update_withContext_missing_context_webhook_test.go b/test/integration/admission/apiV4_update_withContext_andUnknownContext_test.go
similarity index 77%
rename from test/integration/admissionwebhook/api_v4_update_withContext_missing_context_webhook_test.go
rename to test/integration/admission/apiV4_update_withContext_andUnknownContext_test.go
index 9d573b8c1..758c5769a 100644
--- a/test/integration/admissionwebhook/api_v4_update_withContext_missing_context_webhook_test.go
+++ b/test/integration/admission/apiV4_update_withContext_andUnknownContext_test.go
@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
"context"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ v4 "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/v4"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
@@ -27,22 +28,23 @@ import (
"k8s.io/apimachinery/pkg/types"
)
-var _ = Describe("Webhook", labels.WithContext, func() {
- timeout := constants.EventualTimeout / 10
+var _ = Describe("Validate update", labels.WithContext, func() {
interval := constants.Interval
-
ctx := context.Background()
+ admissionCtrl := v4.AdmissionCtrl{}
- It("should get errors for API update, missing management context", func() {
+ It("should return error on API update with missing management context", func() {
fixtures := fixture.
Builder().
WithAPIv4(constants.ApiV4WithContextFile).
Build().
Apply()
- By("Check API update validation")
+ By("checking that API update does not pass validation")
+
Consistently(func() error {
api := new(v1alpha1.ApiV4Definition)
+
if err := manager.Client().Get(ctx, types.NamespacedName{
Name: fixtures.APIv4.Name,
Namespace: fixtures.APIv4.Namespace,
@@ -50,8 +52,8 @@ var _ = Describe("Webhook", labels.WithContext, func() {
return err
}
- _, err := api.ValidateUpdate(nil)
+ _, err := admissionCtrl.ValidateUpdate(ctx, api, api)
return err
- }, timeout, interval).ShouldNot(Succeed())
+ }, constants.ConsistentTimeout, interval).ShouldNot(Succeed())
})
})
diff --git a/test/integration/admission/apiV4_update_withContext_andUnknownMember_test.go b/test/integration/admission/apiV4_update_withContext_andUnknownMember_test.go
new file mode 100644
index 000000000..7d93cdfce
--- /dev/null
+++ b/test/integration/admission/apiV4_update_withContext_andUnknownMember_test.go
@@ -0,0 +1,79 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 admission
+
+import (
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/base"
+ apiV4 "github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/v4"
+ v4 "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/v4"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/errors"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/assert"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/random"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Validate create", labels.WithContext, func() {
+ interval := constants.Interval
+ ctx := context.Background()
+ admissionCtrl := v4.AdmissionCtrl{}
+
+ It("should return warning on API creation with unknown category", func() {
+ fixtures := fixture.
+ Builder().
+ WithAPIv4(constants.ApiV4).
+ WithContext(constants.ContextWithCredentialsFile).
+ Build().
+ Apply()
+
+ By("preparing API for import")
+
+ fixtures.APIv4.Spec.DefinitionContext = apiV4.NewDefaultKubernetesContext()
+ fixtures.APIv4.PopulateIDs(fixtures.Context)
+
+ By("adding an unknown member to the API")
+
+ unknownMemberName := random.GetName()
+
+ fixtures.APIv4.Spec.Members = []*base.Member{
+ base.NewGraviteeMember(unknownMemberName, "REVIEWER"),
+ }
+
+ By("checking that API validation returns warnings")
+
+ Eventually(func() error {
+ warnings, err := admissionCtrl.ValidateUpdate(ctx, fixtures.APIv4, fixtures.APIv4)
+ if err != nil {
+ return err
+ }
+ if err = assert.SliceOfSize("warnings", warnings, 1); err != nil {
+ return err
+ }
+ return assert.Equals(
+ "warning",
+ errors.NewWarning(
+ "Member [%s] of source [gravitee] could not be found in organization [DEFAULT]",
+ unknownMemberName,
+ ).Error(),
+ warnings[0],
+ )
+ }, constants.EventualTimeout, interval).Should(Succeed())
+ })
+})
diff --git a/test/integration/admissionwebhook/api_v4_update_withoutContext_invalid_path_webhook_test.go b/test/integration/admission/apiV4_update_withoutContext_andConflictingPath_test.go
similarity index 77%
rename from test/integration/admissionwebhook/api_v4_update_withoutContext_invalid_path_webhook_test.go
rename to test/integration/admission/apiV4_update_withoutContext_andConflictingPath_test.go
index 5b9596d57..4debcac57 100644
--- a/test/integration/admissionwebhook/api_v4_update_withoutContext_invalid_path_webhook_test.go
+++ b/test/integration/admission/apiV4_update_withoutContext_andConflictingPath_test.go
@@ -12,12 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
+ "context"
+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ v4 "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/api/v4"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
@@ -25,11 +28,12 @@ import (
. "github.com/onsi/gomega"
)
-var _ = Describe("Webhook", labels.WithContext, func() {
- timeout := constants.EventualTimeout / 10
+var _ = Describe("Validate update", labels.WithoutContext, func() {
interval := constants.Interval
+ ctx := context.Background()
+ admissionCtrl := v4.AdmissionCtrl{}
- It("should get errors for API update because of existing path", func() {
+ It("should return error on API update with conflicting path", func() {
fixtures := fixture.
Builder().
WithAPIv4(constants.ApiV4).
@@ -44,11 +48,9 @@ var _ = Describe("Webhook", labels.WithContext, func() {
Namespace: fixtures.APIv4.Namespace,
},
}
-
fixtures.APIv4.Spec.DeepCopyInto(&api.Spec)
-
- _, err := api.ValidateUpdate(nil)
+ _, err := admissionCtrl.ValidateUpdate(ctx, api, api)
return err
- }, timeout, interval).ShouldNot(Succeed())
+ }, constants.EventualTimeout, interval).ShouldNot(Succeed())
})
})
diff --git a/test/integration/admission/application_create_withContext_andUnknownContext_test.go b/test/integration/admission/application_create_withContext_andUnknownContext_test.go
new file mode 100644
index 000000000..d3852c616
--- /dev/null
+++ b/test/integration/admission/application_create_withContext_andUnknownContext_test.go
@@ -0,0 +1,56 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 admission
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/application"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/assert"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Validate create", labels.WithContext, func() {
+ interval := constants.Interval
+ ctx := context.Background()
+ admissionCtrl := application.AdmissionCtrl{}
+
+ It("should return error on application creation with missing management context", func() {
+ fixtures := fixture.
+ Builder().
+ WithApplication(constants.Application).
+ Build().
+ Apply()
+
+ By("checking that application does not pass validation")
+
+ Consistently(func() error {
+ _, err := admissionCtrl.ValidateCreate(ctx, fixtures.Application)
+ return assert.Equals(
+ "error",
+ fmt.Errorf(
+ "resource [%s] references management context [default/dev-ctx] that doesn't exist in the cluster",
+ fixtures.Application.Name,
+ ),
+ err,
+ )
+ }, constants.ConsistentTimeout, interval).ShouldNot(Succeed())
+ })
+})
diff --git a/test/integration/admissionwebhook/managementcontext_create_missing_secret_webhook_test.go b/test/integration/admission/managementcontext_create_withBadCredentials_test.go
similarity index 58%
rename from test/integration/admissionwebhook/managementcontext_create_missing_secret_webhook_test.go
rename to test/integration/admission/managementcontext_create_withBadCredentials_test.go
index 099a8dc13..075d705c0 100644
--- a/test/integration/admissionwebhook/managementcontext_create_missing_secret_webhook_test.go
+++ b/test/integration/admission/managementcontext_create_withBadCredentials_test.go
@@ -12,55 +12,50 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
"context"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/assert"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
- "github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
- "k8s.io/apimachinery/pkg/types"
-
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
- "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/manager"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/mctx"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/errors"
)
-var _ = Describe("Webhook", labels.WithoutContext, func() {
- timeout := constants.EventualTimeout / 10
+var _ = Describe("Validate create", labels.WithContext, func() {
interval := constants.Interval
+ ctx := context.Background()
+ admissionCtrl := mctx.AdmissionCtrl{}
+
+ It("should return error if secret is missing", func() {
+
+ By("setting invalid credentials onto context")
- It("Show throws error secret is missing", func() {
- cli := manager.Client()
fixtures := fixture.Builder().
- WithContext(constants.ContextWithSecretFile).
+ WithContext(constants.ContextWithBadCredentialsFile).
Build().
Apply()
- ctx := new(v1alpha1.ManagementContext)
- Eventually(func() error {
- err := cli.Get(context.Background(), types.NamespacedName{
- Namespace: fixtures.Context.Namespace,
- Name: fixtures.Context.Name,
- }, ctx)
-
- if err != nil {
- return err
- }
- return nil
- }).Should(Succeed())
+ By("validating the context")
Consistently(func() error {
- warnings, err := ctx.ValidateCreate()
- if len(warnings) != 0 {
- return nil
- }
-
- return err
- }, timeout, interval).ShouldNot(Succeed())
+ _, err := admissionCtrl.ValidateCreate(ctx, fixtures.Context)
+ return assert.Equals(
+ "error",
+ errors.NewSevere(
+ "bad credentials for context [%s]",
+ fixtures.Context.Name,
+ ),
+ err,
+ )
+ }, constants.ConsistentTimeout, interval).Should(Succeed())
})
})
diff --git a/test/integration/admissionwebhook/api_v2_mutate_withContext_withoutNamespace.go b/test/integration/admission/managementcontext_create_withEnvironmentNotFound_test.go
similarity index 57%
rename from test/integration/admissionwebhook/api_v2_mutate_withContext_withoutNamespace.go
rename to test/integration/admission/managementcontext_create_withEnvironmentNotFound_test.go
index 4af227e3a..027091cda 100644
--- a/test/integration/admissionwebhook/api_v2_mutate_withContext_withoutNamespace.go
+++ b/test/integration/admission/managementcontext_create_withEnvironmentNotFound_test.go
@@ -12,34 +12,43 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
- "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
+ "context"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/assert"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
- "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
+
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
+
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/mctx"
)
-var _ = Describe("Mutate", labels.WithContext, func() {
- It("should set context namespace to api namespace if empty", func() {
- fixtures := fixture.
- Builder().
- WithAPI(constants.ApiWithContextFile).
- WithContext(constants.ContextWithCredentialsFile).
- Build()
+var _ = Describe("Validate create", labels.WithContext, func() {
+ interval := constants.Interval
+ ctx := context.Background()
+ admissionCtrl := mctx.AdmissionCtrl{}
- By("removing namespace from context reference")
+ It("should return error if secret is missing", func() {
- fixtures.API.Spec.Context.Namespace = ""
+ By("setting unknown environment onto context")
- By("applying defaults")
+ fixtures := fixture.Builder().
+ WithContext(constants.ContextWithCredentialsFile).
+ Build()
- Expect(fixtures.API.Namespace).ToNot(BeEmpty())
+ fixtures.Context.Spec.EnvID = "unknown"
- fixtures.API.Default()
+ By("validating the context")
- Expect(fixtures.API.Spec.Context.Namespace).To(Equal(fixtures.API.Namespace))
+ Consistently(func() error {
+ _, err := admissionCtrl.ValidateCreate(ctx, fixtures.Context)
+ return assert.NotNil("error", err)
+ }, constants.ConsistentTimeout, interval).Should(Succeed())
})
})
diff --git a/test/integration/admissionwebhook/api_v4_mutate_withContext_withoutNamespace.go b/test/integration/admission/managementcontext_create_withMissingSecret_test.go
similarity index 62%
rename from test/integration/admissionwebhook/api_v4_mutate_withContext_withoutNamespace.go
rename to test/integration/admission/managementcontext_create_withMissingSecret_test.go
index e28272bc3..e1dd99944 100644
--- a/test/integration/admissionwebhook/api_v4_mutate_withContext_withoutNamespace.go
+++ b/test/integration/admission/managementcontext_create_withMissingSecret_test.go
@@ -12,34 +12,35 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
- "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
+ "context"
+
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
- "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
+
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
-)
-
-var _ = Describe("Mutate", labels.WithContext, func() {
- It("should set context namespace to api namespace if empty", func() {
- fixtures := fixture.
- Builder().
- WithAPIv4(constants.ApiV4WithContextFile).
- WithContext(constants.ContextWithCredentialsFile).
- Build()
-
- By("removing namespace from context reference")
- fixtures.APIv4.Spec.Context.Namespace = ""
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
- By("applying defaults")
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/mctx"
+)
- Expect(fixtures.APIv4.Namespace).ToNot(BeEmpty())
+var _ = Describe("Validate create", labels.WithContext, func() {
+ interval := constants.Interval
+ ctx := context.Background()
+ admissionCtrl := mctx.AdmissionCtrl{}
- fixtures.APIv4.Default()
+ It("should return error if secret is missing", func() {
+ fixtures := fixture.Builder().
+ WithContext(constants.ContextWithSecretFile).
+ Build()
- Expect(fixtures.APIv4.Spec.Context.Namespace).To(Equal(fixtures.APIv4.Namespace))
+ Consistently(func() error {
+ _, err := admissionCtrl.ValidateCreate(ctx, fixtures.Context)
+ return err
+ }, constants.ConsistentTimeout, interval).Should(HaveOccurred())
})
})
diff --git a/test/integration/admissionwebhook/managementcontext_create_unavailable_apim_webhook_test.go b/test/integration/admission/managementcontext_create_withNetworkError_test.go
similarity index 71%
rename from test/integration/admissionwebhook/managementcontext_create_unavailable_apim_webhook_test.go
rename to test/integration/admission/managementcontext_create_withNetworkError_test.go
index 05022b322..25b590bcf 100644
--- a/test/integration/admissionwebhook/managementcontext_create_unavailable_apim_webhook_test.go
+++ b/test/integration/admission/managementcontext_create_withNetworkError_test.go
@@ -12,14 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
"context"
- "errors"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/management"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/mctx"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/assert"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/random"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
@@ -32,12 +33,13 @@ import (
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/manager"
)
-var _ = Describe("Webhook", labels.WithoutContext, func() {
- timeout := constants.EventualTimeout / 10
+var _ = Describe("Validate create", labels.WithContext, func() {
interval := constants.Interval
+ ctx := context.Background()
+ admissionCtrl := mctx.AdmissionCtrl{}
+ cli := manager.Client()
- It("Show gives warning when APIM is not accessible", func() {
- cli := manager.Client()
+ It("should return warning if APIM is not accessible", func() {
mCtx := &v1alpha1.ManagementContext{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
@@ -47,8 +49,8 @@ var _ = Describe("Webhook", labels.WithoutContext, func() {
Spec: v1alpha1.ManagementContextSpec{
Context: &management.Context{
BaseUrl: "https://gko.example.com",
- EnvId: "DEFAULT",
- OrgId: "DEFAULT",
+ EnvID: "DEFAULT",
+ OrgID: "DEFAULT",
Auth: &management.Auth{
BearerToken: "test",
},
@@ -58,26 +60,16 @@ var _ = Describe("Webhook", labels.WithoutContext, func() {
Expect(cli.Create(context.Background(), mCtx)).To(Succeed())
- ctx := new(v1alpha1.ManagementContext)
Eventually(func() error {
- err := cli.Get(context.Background(), types.NamespacedName{
+ return cli.Get(context.Background(), types.NamespacedName{
Namespace: mCtx.Namespace,
Name: mCtx.Name,
- }, ctx)
-
- if err != nil {
- return err
- }
- return nil
- }).Should(Succeed())
+ }, mCtx)
+ }, constants.EventualTimeout, interval).Should(Succeed())
Consistently(func() error {
- warnings, _ := ctx.ValidateCreate()
- if len(warnings) != 1 {
- return nil
- }
-
- return errors.New(warnings[0])
- }, timeout, interval).ShouldNot(Succeed())
+ warnings, _ := admissionCtrl.ValidateCreate(ctx, mCtx)
+ return assert.SliceOfSize("warnings", warnings, 1)
+ }, constants.ConsistentTimeout, interval).Should(Succeed())
})
})
diff --git a/test/integration/admissionwebhook/managementcontext_update_missing_secret_webhook_test.go b/test/integration/admission/managementcontext_update_withMissingSecret_test.go
similarity index 70%
rename from test/integration/admissionwebhook/managementcontext_update_missing_secret_webhook_test.go
rename to test/integration/admission/managementcontext_update_withMissingSecret_test.go
index 1f4d58f76..3f07723b7 100644
--- a/test/integration/admissionwebhook/managementcontext_update_missing_secret_webhook_test.go
+++ b/test/integration/admission/managementcontext_update_withMissingSecret_test.go
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
"context"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/mctx"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
@@ -30,37 +31,29 @@ import (
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/manager"
)
-var _ = Describe("Webhook", labels.WithoutContext, func() {
- timeout := constants.EventualTimeout / 10
+var _ = Describe("Validate update", labels.WithContext, func() {
interval := constants.Interval
+ admissionCtrl := mctx.AdmissionCtrl{}
+ ctx := context.Background()
+ cli := manager.Client()
- It("Show throws error secret is missing", func() {
- cli := manager.Client()
+ It("should return error if secret is missing", func() {
fixtures := fixture.Builder().
WithContext(constants.ContextWithSecretFile).
Build().
Apply()
- ctx := new(v1alpha1.ManagementContext)
+ mCtx := new(v1alpha1.ManagementContext)
Eventually(func() error {
- err := cli.Get(context.Background(), types.NamespacedName{
+ return cli.Get(context.Background(), types.NamespacedName{
Namespace: fixtures.Context.Namespace,
Name: fixtures.Context.Name,
- }, ctx)
-
- if err != nil {
- return err
- }
- return nil
- }).Should(Succeed())
+ }, mCtx)
+ }, constants.EventualTimeout, interval).Should(Succeed())
Consistently(func() error {
- warnings, err := ctx.ValidateUpdate(nil)
- if len(warnings) != 0 {
- return nil
- }
-
+ _, err := admissionCtrl.ValidateCreate(ctx, mCtx)
return err
- }, timeout, interval).ShouldNot(Succeed())
+ }, constants.ConsistentTimeout, interval).ShouldNot(Succeed())
})
})
diff --git a/test/integration/admissionwebhook/managementcontext_update_unavailable_apim_webhook_test.go b/test/integration/admission/managementcontext_update_withNetworkError_test.go
similarity index 71%
rename from test/integration/admissionwebhook/managementcontext_update_unavailable_apim_webhook_test.go
rename to test/integration/admission/managementcontext_update_withNetworkError_test.go
index eb872e3b1..bedbab89a 100644
--- a/test/integration/admissionwebhook/managementcontext_update_unavailable_apim_webhook_test.go
+++ b/test/integration/admission/managementcontext_update_withNetworkError_test.go
@@ -12,14 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package admission
import (
"context"
- "errors"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/management"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/internal/admission/mctx"
+ "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/assert"
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/random"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
@@ -32,12 +33,13 @@ import (
"github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/manager"
)
-var _ = Describe("Webhook", labels.WithoutContext, func() {
- timeout := constants.EventualTimeout / 10
+var _ = Describe("Validate update", labels.WithContext, func() {
interval := constants.Interval
+ admissionCtrl := mctx.AdmissionCtrl{}
+ ctx := context.Background()
+ cli := manager.Client()
- It("Show gives warning when APIM is not accessible", func() {
- cli := manager.Client()
+ It("should return warnings if APIM is not accessible", func() {
mCtx := &v1alpha1.ManagementContext{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
@@ -47,8 +49,8 @@ var _ = Describe("Webhook", labels.WithoutContext, func() {
Spec: v1alpha1.ManagementContextSpec{
Context: &management.Context{
BaseUrl: "https://gko.example.com",
- EnvId: "DEFAULT",
- OrgId: "DEFAULT",
+ EnvID: "DEFAULT",
+ OrgID: "DEFAULT",
Auth: &management.Auth{
BearerToken: "test",
},
@@ -58,26 +60,16 @@ var _ = Describe("Webhook", labels.WithoutContext, func() {
Expect(cli.Create(context.Background(), mCtx)).To(Succeed())
- ctx := new(v1alpha1.ManagementContext)
Eventually(func() error {
- err := cli.Get(context.Background(), types.NamespacedName{
+ return cli.Get(context.Background(), types.NamespacedName{
Namespace: mCtx.Namespace,
Name: mCtx.Name,
- }, ctx)
-
- if err != nil {
- return err
- }
- return nil
- }).Should(Succeed())
+ }, mCtx)
+ }, constants.EventualTimeout, interval).Should(Succeed())
Consistently(func() error {
- warnings, _ := ctx.ValidateUpdate(nil)
- if len(warnings) != 1 {
- return nil
- }
-
- return errors.New(warnings[0])
- }, timeout, interval).ShouldNot(Succeed())
+ warnings, _ := admissionCtrl.ValidateUpdate(ctx, mCtx, mCtx)
+ return assert.SliceOfSize("warnings", warnings, 1)
+ }, constants.ConsistentTimeout, interval).ShouldNot(Succeed())
})
})
diff --git a/test/integration/admission/suite_test.go b/test/integration/admission/suite_test.go
new file mode 100644
index 000000000..14812be75
--- /dev/null
+++ b/test/integration/admission/suite_test.go
@@ -0,0 +1,38 @@
+// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+//
+// 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 admission
+
+import (
+ "testing"
+ "time"
+
+ "github.com/onsi/gomega/gexec"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ //+kubebuilder:scaffold:imports
+)
+
+func TestResources(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Admission Suite")
+}
+
+var _ = SynchronizedAfterSuite(func() {
+ By("Tearing down the test environment")
+ gexec.KillAndWait(5 * time.Second)
+}, func() {
+ // NOSONAR ignore this noop func
+})
diff --git a/test/integration/admissionwebhook/application_mutate_withContext_withoutNamespace.go b/test/integration/admissionwebhook/application_mutate_withContext_withoutNamespace.go
deleted file mode 100644
index 276c8d38c..000000000
--- a/test/integration/admissionwebhook/application_mutate_withContext_withoutNamespace.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2015 The Gravitee team (http://gravitee.io)
-//
-// 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 admissionwebhook
-
-import (
- "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/constants"
- "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/fixture"
- "github.com/gravitee-io/gravitee-kubernetes-operator/test/internal/integration/labels"
- . "github.com/onsi/ginkgo/v2"
- . "github.com/onsi/gomega"
-)
-
-var _ = Describe("Mutate", labels.WithContext, func() {
- It("should set context namespace to application namespace if empty", func() {
- fixtures := fixture.
- Builder().
- WithApplication(constants.Application).
- WithContext(constants.ContextWithCredentialsFile).
- Build()
-
- By("removing namespace from context reference")
-
- fixtures.Application.Spec.Context.Namespace = ""
-
- By("applying defaults")
-
- Expect(fixtures.Application.Namespace).ToNot(BeEmpty())
-
- fixtures.Application.Default()
-
- Expect(fixtures.Application.Spec.Context.Namespace).To(Equal(fixtures.Application.Namespace))
- })
-})
diff --git a/test/integration/apidefinition/create_withContext_andMardownPage_test.go b/test/integration/apidefinition/create_withContext_andMardownPage_test.go
index bc0247316..6d3c8e0f0 100644
--- a/test/integration/apidefinition/create_withContext_andMardownPage_test.go
+++ b/test/integration/apidefinition/create_withContext_andMardownPage_test.go
@@ -42,12 +42,12 @@ var _ = Describe("Create", labels.WithContext, func() {
Apply()
apim := apim.NewClient(ctx)
- apiId := fixtures.API.Status.ID
+ apiID := fixtures.API.Status.ID
By("checking pages number in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPI(apiId)
+ pages, err := apim.Pages.FindByAPI(apiID)
if err != nil {
return err
}
@@ -61,7 +61,7 @@ var _ = Describe("Create", labels.WithContext, func() {
Expect(markdown).ToNot(BeNil())
Eventually(func() error {
- pages, err := apim.Pages.FindByAPI(apiId, model.NewPageQuery().WithType("MARKDOWN"))
+ pages, err := apim.Pages.FindByAPI(apiID, model.NewPageQuery().WithType("MARKDOWN"))
if err != nil {
return err
}
diff --git a/test/integration/apidefinition/create_withContext_MemberWithoutRole_test.go b/test/integration/apidefinition/create_withContext_andMemberWithoutRole_test.go
similarity index 91%
rename from test/integration/apidefinition/create_withContext_MemberWithoutRole_test.go
rename to test/integration/apidefinition/create_withContext_andMemberWithoutRole_test.go
index 572075b9e..a4e8eb514 100644
--- a/test/integration/apidefinition/create_withContext_MemberWithoutRole_test.go
+++ b/test/integration/apidefinition/create_withContext_andMemberWithoutRole_test.go
@@ -16,7 +16,7 @@ package apidefinition
import (
"context"
- "sort"
+ "strings"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/base"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim/model"
@@ -72,13 +72,13 @@ var _ = Describe("Update", labels.WithContext, func() {
exportedMembers := apiExport.Spec.Members
- By("sorting exported API members by source")
-
- sort.Slice(exportedMembers, func(i, j int) bool {
- return exportedMembers[i].Source < exportedMembers[j].Source
- })
-
- return assert.Equals("members", expectedMembers, exportedMembers)
+ return assert.SliceEqualsSorted(
+ "members",
+ expectedMembers, exportedMembers,
+ func(a, b *base.Member) int {
+ return strings.Compare(a.Source+a.SourceID, b.Source+b.SourceID)
+ },
+ )
}, timeout, interval).Should(Succeed(), fixtures.API.Name)
})
})
diff --git a/test/integration/apidefinition/create_withContext_andPageWithACL_test.go b/test/integration/apidefinition/create_withContext_andPageWithACL_test.go
index 451cc2d3d..2fac22946 100644
--- a/test/integration/apidefinition/create_withContext_andPageWithACL_test.go
+++ b/test/integration/apidefinition/create_withContext_andPageWithACL_test.go
@@ -46,7 +46,7 @@ var _ = Describe("Create", labels.WithContext, func() {
acl := []base.AccessControl{
{
- ReferenceId: groupName,
+ ReferenceID: groupName,
ReferenceType: "GROUP",
},
}
diff --git a/test/integration/apidefinition/create_withContext_andSwaggerHTTPFetcher_test.go b/test/integration/apidefinition/create_withContext_andSwaggerHTTPFetcher_test.go
index 73f5a184f..8b2f8cfcb 100644
--- a/test/integration/apidefinition/create_withContext_andSwaggerHTTPFetcher_test.go
+++ b/test/integration/apidefinition/create_withContext_andSwaggerHTTPFetcher_test.go
@@ -42,12 +42,12 @@ var _ = Describe("Create", labels.WithContext, func() {
Apply()
apim := apim.NewClient(ctx)
- apiId := fixtures.API.Status.ID
+ apiID := fixtures.API.Status.ID
By("checking pages number in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPI(apiId)
+ pages, err := apim.Pages.FindByAPI(apiID)
if err != nil {
return err
}
@@ -57,7 +57,7 @@ var _ = Describe("Create", labels.WithContext, func() {
By("checking swagger content in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPI(apiId, model.NewPageQuery().WithType("SWAGGER"))
+ pages, err := apim.Pages.FindByAPI(apiID, model.NewPageQuery().WithType("SWAGGER"))
if err != nil {
return err
}
diff --git a/test/integration/apidefinition/create_withContext_fromExport_test.go b/test/integration/apidefinition/create_withContext_fromExport_test.go
index bc09db2c6..8da1bae65 100644
--- a/test/integration/apidefinition/create_withContext_fromExport_test.go
+++ b/test/integration/apidefinition/create_withContext_fromExport_test.go
@@ -41,7 +41,7 @@ var _ = Describe("Create", labels.WithContext, func() {
It("should update existing api in management API", func() {
fixtures := fixture.Builder().
WithContext(constants.ContextWithSecretFile).
- WithAPI(constants.ApiWithIds).
+ WithAPI(constants.ApiWithIDs).
Build()
By("creating API in management api")
diff --git a/test/integration/apidefinition/subscribe_withContext_test.go b/test/integration/apidefinition/subscribe_withContext_test.go
index 6d18de10a..1e01297e4 100644
--- a/test/integration/apidefinition/subscribe_withContext_test.go
+++ b/test/integration/apidefinition/subscribe_withContext_test.go
@@ -69,16 +69,16 @@ var _ = Describe("Subscribe", labels.WithContext, func() {
api, err := apim.APIs.GetByID(fixtures.API.Status.ID)
Expect(err).ToNot(HaveOccurred())
Expect(api.Plans).ToNot(BeEmpty())
- planID := api.Plans[0].Id
+ planID := api.Plans[0].ID
By("calling rest API expecting to application to subscribe to API")
- subscription, err := apim.Subscriptions.Subscribe(fixtures.API.Status.ID, app.Id, planID)
+ subscription, err := apim.Subscriptions.Subscribe(fixtures.API.Status.ID, app.ID, planID)
Expect(err).ToNot(HaveOccurred())
By("calling rest API expecting to find subscription API key")
- keys, err := apim.Subscriptions.GetApiKeys(fixtures.API.Status.ID, subscription.Id)
+ keys, err := apim.Subscriptions.GetApiKeys(fixtures.API.Status.ID, subscription.ID)
Expect(err).ToNot(HaveOccurred())
Expect(keys).ToNot(BeEmpty())
key := keys[0].Key
diff --git a/test/integration/apidefinition/update_withContext_andMardownPage_test.go b/test/integration/apidefinition/update_withContext_andMardownPage_test.go
index 623683f6e..8b94ab6c5 100644
--- a/test/integration/apidefinition/update_withContext_andMardownPage_test.go
+++ b/test/integration/apidefinition/update_withContext_andMardownPage_test.go
@@ -43,7 +43,7 @@ var _ = Describe("Update", labels.WithContext, func() {
Apply()
apim := apim.NewClient(ctx)
- apiId := fixtures.API.Status.ID
+ apiID := fixtures.API.Status.ID
By("updating markdown content")
@@ -58,7 +58,7 @@ var _ = Describe("Update", labels.WithContext, func() {
By("checking pages number in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPI(apiId)
+ pages, err := apim.Pages.FindByAPI(apiID)
if err != nil {
return err
}
@@ -70,7 +70,7 @@ var _ = Describe("Update", labels.WithContext, func() {
Expect(markdown).ToNot(BeNil())
Eventually(func() error {
- pages, err := apim.Pages.FindByAPI(apiId, model.NewPageQuery().WithType("MARKDOWN"))
+ pages, err := apim.Pages.FindByAPI(apiID, model.NewPageQuery().WithType("MARKDOWN"))
if err != nil {
return err
}
diff --git a/test/integration/apidefinition/update_withContext_changingMemberRole_test.go b/test/integration/apidefinition/update_withContext_changingMemberRole_test.go
index 8baf0da4e..f0f5fea06 100644
--- a/test/integration/apidefinition/update_withContext_changingMemberRole_test.go
+++ b/test/integration/apidefinition/update_withContext_changingMemberRole_test.go
@@ -16,6 +16,7 @@ package apidefinition
import (
"context"
+ "strings"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/base"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim/model"
@@ -88,7 +89,13 @@ var _ = Describe("Update", labels.WithContext, func() {
if err != nil {
return err
}
- return assert.Equals("members", expectedMembers, export.Spec.Members)
+ return assert.SliceEqualsSorted(
+ "members",
+ expectedMembers, export.Spec.Members,
+ func(a, b *base.Member) int {
+ return strings.Compare(a.SourceID, b.SourceID)
+ },
+ )
}, timeout, interval).Should(Succeed(), fixtures.API.Name)
})
})
diff --git a/test/integration/apidefinition/update_withContext_deletingPage_test.go b/test/integration/apidefinition/update_withContext_deletingPage_test.go
index e6df31d0b..63b0fddac 100644
--- a/test/integration/apidefinition/update_withContext_deletingPage_test.go
+++ b/test/integration/apidefinition/update_withContext_deletingPage_test.go
@@ -43,12 +43,12 @@ var _ = Describe("Update", labels.WithContext, func() {
Apply()
apim := apim.NewClient(ctx)
- apiId := fixtures.API.Status.ID
+ apiID := fixtures.API.Status.ID
By("checking pages number in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPI(apiId)
+ pages, err := apim.Pages.FindByAPI(apiID)
if err != nil {
return err
}
@@ -66,7 +66,7 @@ var _ = Describe("Update", labels.WithContext, func() {
By("checking that markdown page has been deleted in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPI(apiId, model.NewPageQuery().WithType("MARKDOWN"))
+ pages, err := apim.Pages.FindByAPI(apiID, model.NewPageQuery().WithType("MARKDOWN"))
if err != nil {
return err
}
diff --git a/test/integration/apidefinition/update_withContext_removingMember_test.go b/test/integration/apidefinition/update_withContext_removingMember_test.go
index 75daedaae..ce2dfc30a 100644
--- a/test/integration/apidefinition/update_withContext_removingMember_test.go
+++ b/test/integration/apidefinition/update_withContext_removingMember_test.go
@@ -16,6 +16,7 @@ package apidefinition
import (
"context"
+ "strings"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/model/api/base"
"github.com/gravitee-io/gravitee-kubernetes-operator/internal/apim/model"
@@ -71,7 +72,13 @@ var _ = Describe("Update", labels.WithContext, func() {
if err != nil {
return err
}
- return assert.Equals("members", []*base.Member{primaryOwner, saMember}, export.Spec.Members)
+ return assert.SliceEqualsSorted(
+ "members",
+ []*base.Member{primaryOwner, saMember}, export.Spec.Members,
+ func(a, b *base.Member) int {
+ return strings.Compare(a.SourceID, b.SourceID)
+ },
+ )
}, timeout, interval).Should(Succeed(), fixtures.API.Name)
By("removing service account from API members")
diff --git a/test/integration/apidefinition/v4-create_withContext_andMardownPage_test.go b/test/integration/apidefinition/v4-create_withContext_andMardownPage_test.go
index c604d60bc..1c5bb6f61 100644
--- a/test/integration/apidefinition/v4-create_withContext_andMardownPage_test.go
+++ b/test/integration/apidefinition/v4-create_withContext_andMardownPage_test.go
@@ -42,12 +42,12 @@ var _ = Describe("Create", labels.WithContext, func() {
Apply()
apim := apim.NewClient(ctx)
- apiId := fixtures.APIv4.Status.ID
+ apiID := fixtures.APIv4.Status.ID
By("checking pages number in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPIV4(apiId)
+ pages, err := apim.Pages.FindByAPIV4(apiID)
if err != nil {
return err
}
@@ -61,7 +61,7 @@ var _ = Describe("Create", labels.WithContext, func() {
Expect(markdown).ToNot(BeNil())
Eventually(func() error {
- pages, err := apim.Pages.FindByAPIV4(apiId, model.NewPageQuery().WithType("MARKDOWN"))
+ pages, err := apim.Pages.FindByAPIV4(apiID, model.NewPageQuery().WithType("MARKDOWN"))
if err != nil {
return err
}
diff --git a/test/integration/apidefinition/v4-update_withContext_andMardownPage_test.go b/test/integration/apidefinition/v4-update_withContext_andMardownPage_test.go
index aea0b7a89..9e50e11b4 100644
--- a/test/integration/apidefinition/v4-update_withContext_andMardownPage_test.go
+++ b/test/integration/apidefinition/v4-update_withContext_andMardownPage_test.go
@@ -43,7 +43,7 @@ var _ = Describe("Update", labels.WithContext, func() {
Apply()
apim := apim.NewClient(ctx)
- apiId := fixtures.APIv4.Status.ID
+ apiID := fixtures.APIv4.Status.ID
By("updating markdown content")
@@ -58,7 +58,7 @@ var _ = Describe("Update", labels.WithContext, func() {
By("checking pages number in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPIV4(apiId)
+ pages, err := apim.Pages.FindByAPIV4(apiID)
if err != nil {
return err
}
@@ -70,7 +70,7 @@ var _ = Describe("Update", labels.WithContext, func() {
Expect(markdown).ToNot(BeNil())
Eventually(func() error {
- pages, err := apim.Pages.FindByAPI(apiId, model.NewPageQuery().WithType("MARKDOWN"))
+ pages, err := apim.Pages.FindByAPI(apiID, model.NewPageQuery().WithType("MARKDOWN"))
if err != nil {
return err
}
diff --git a/test/integration/apidefinition/v4-update_withContext_deletingPage_test.go b/test/integration/apidefinition/v4-update_withContext_deletingPage_test.go
index a2f4301c3..6234c3ad2 100644
--- a/test/integration/apidefinition/v4-update_withContext_deletingPage_test.go
+++ b/test/integration/apidefinition/v4-update_withContext_deletingPage_test.go
@@ -43,12 +43,12 @@ var _ = Describe("Update", labels.WithContext, func() {
Apply()
apim := apim.NewClient(ctx)
- apiId := fixtures.APIv4.Status.ID
+ apiID := fixtures.APIv4.Status.ID
By("checking pages number in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPIV4(apiId)
+ pages, err := apim.Pages.FindByAPIV4(apiID)
if err != nil {
return err
}
@@ -66,7 +66,7 @@ var _ = Describe("Update", labels.WithContext, func() {
By("checking that markdown page has been deleted in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPIV4(apiId, model.NewPageQuery().WithType("MARKDOWN"))
+ pages, err := apim.Pages.FindByAPIV4(apiID, model.NewPageQuery().WithType("MARKDOWN"))
if err != nil {
return err
}
diff --git a/test/integration/apidefinition/v4_create_withContext_andSwaggerHTTPFetcher_test.go b/test/integration/apidefinition/v4_create_withContext_andSwaggerHTTPFetcher_test.go
index 321df8116..4b9e8979a 100644
--- a/test/integration/apidefinition/v4_create_withContext_andSwaggerHTTPFetcher_test.go
+++ b/test/integration/apidefinition/v4_create_withContext_andSwaggerHTTPFetcher_test.go
@@ -42,12 +42,12 @@ var _ = Describe("Create", labels.WithContext, func() {
Apply()
apim := apim.NewClient(ctx)
- apiId := fixtures.APIv4.Status.ID
+ apiID := fixtures.APIv4.Status.ID
By("checking pages number in APIM")
Eventually(func() error {
- pages, err := apim.Pages.FindByAPIV4(apiId)
+ pages, err := apim.Pages.FindByAPIV4(apiID)
if err != nil {
return err
}
@@ -60,7 +60,7 @@ var _ = Describe("Create", labels.WithContext, func() {
Expect(swagger).ToNot(BeNil())
Eventually(func() error {
- pages, err := apim.Pages.FindByAPIV4(apiId, model.NewPageQuery().WithType("SWAGGER"))
+ pages, err := apim.Pages.FindByAPIV4(apiID, model.NewPageQuery().WithType("SWAGGER"))
if err != nil {
return err
}
diff --git a/test/integration/apidefinition/v4_subscribe_withContext_test.go b/test/integration/apidefinition/v4_subscribe_withContext_test.go
index 49471ece0..369357908 100644
--- a/test/integration/apidefinition/v4_subscribe_withContext_test.go
+++ b/test/integration/apidefinition/v4_subscribe_withContext_test.go
@@ -71,12 +71,12 @@ var _ = Describe("Subscribe", labels.WithContext, func() {
By("calling rest API expecting to application to subscribe to API")
- subscription, err := apim.Subscriptions.Subscribe(fixtures.APIv4.Status.ID, app.Id, planID)
+ subscription, err := apim.Subscriptions.Subscribe(fixtures.APIv4.Status.ID, app.ID, planID)
Expect(err).ToNot(HaveOccurred())
By("calling rest API expecting to find subscription API key")
- keys, err := apim.Subscriptions.GetApiKeys(fixtures.APIv4.Status.ID, subscription.Id)
+ keys, err := apim.Subscriptions.GetApiKeys(fixtures.APIv4.Status.ID, subscription.ID)
Expect(err).ToNot(HaveOccurred())
Expect(keys).ToNot(BeEmpty())
key := keys[0].Key
diff --git a/test/integration/admissionwebhook/suite_test.go b/test/integration/webhook/suite_test.go
similarity index 97%
rename from test/integration/admissionwebhook/suite_test.go
rename to test/integration/webhook/suite_test.go
index 4f7063a5b..57460e09d 100644
--- a/test/integration/admissionwebhook/suite_test.go
+++ b/test/integration/webhook/suite_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package webhook
import (
"testing"
diff --git a/test/integration/admissionwebhook/webhook_generate_secretes_withoutContext.go b/test/integration/webhook/webhook_generate_secretes_withoutContext.go
similarity index 99%
rename from test/integration/admissionwebhook/webhook_generate_secretes_withoutContext.go
rename to test/integration/webhook/webhook_generate_secretes_withoutContext.go
index 4c33f690c..b95a8f1ab 100644
--- a/test/integration/admissionwebhook/webhook_generate_secretes_withoutContext.go
+++ b/test/integration/webhook/webhook_generate_secretes_withoutContext.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package webhook
import (
"bytes"
diff --git a/test/integration/admissionwebhook/webhook_patch_admission_webhook_configuration_withoutContext.go b/test/integration/webhook/webhook_patch_admission_webhook_configuration_withoutContext.go
similarity index 99%
rename from test/integration/admissionwebhook/webhook_patch_admission_webhook_configuration_withoutContext.go
rename to test/integration/webhook/webhook_patch_admission_webhook_configuration_withoutContext.go
index e6ba4a9a5..e1597e8dd 100644
--- a/test/integration/admissionwebhook/webhook_patch_admission_webhook_configuration_withoutContext.go
+++ b/test/integration/webhook/webhook_patch_admission_webhook_configuration_withoutContext.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package webhook
import (
"context"
diff --git a/test/integration/admissionwebhook/webhook_patch_secret_withoutContext.go b/test/integration/webhook/webhook_patch_secret_withoutContext.go
similarity index 98%
rename from test/integration/admissionwebhook/webhook_patch_secret_withoutContext.go
rename to test/integration/webhook/webhook_patch_secret_withoutContext.go
index 985bdcb5a..6f9ac8494 100644
--- a/test/integration/admissionwebhook/webhook_patch_secret_withoutContext.go
+++ b/test/integration/webhook/webhook_patch_secret_withoutContext.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package admissionwebhook
+package webhook
import (
"context"
diff --git a/test/internal/integration/apim/apim.go b/test/internal/integration/apim/apim.go
index 5c84daeaf..6dfddd2cb 100644
--- a/test/internal/integration/apim/apim.go
+++ b/test/internal/integration/apim/apim.go
@@ -41,7 +41,7 @@ func NewClient(ctx context.Context) *APIM {
Build().
Context
- apim, err := apim.FromContext(ctx, context.Spec.Context)
+ apim, err := apim.FromContext(ctx, context, context.GetNamespace())
Expect(err).ToNot(HaveOccurred())
subscriptions := service.NewSubscriptions(apim.APIs.Client)
diff --git a/test/internal/integration/assert/assert.go b/test/internal/integration/assert/assert.go
index 43dee14eb..1ee7e4704 100644
--- a/test/internal/integration/assert/assert.go
+++ b/test/internal/integration/assert/assert.go
@@ -19,6 +19,7 @@ import (
"fmt"
"net/http"
"reflect"
+ "slices"
"strings"
"github.com/gravitee-io/gravitee-kubernetes-operator/api/v1alpha1"
@@ -97,6 +98,15 @@ func Equals(field string, expected, given any) error {
return nil
}
+func SliceEqualsSorted[S ~[]E, E any](field string, expected S, given S, comp func(a, b E) int) error {
+ ecp, gcp := make([]E, len(expected)), make([]E, len(given))
+ copy(ecp, given)
+ slices.SortFunc(ecp, comp)
+ copy(gcp, expected)
+ slices.SortFunc(gcp, comp)
+ return Equals(field, ecp, gcp)
+}
+
func NotEmptySlice[T any](field string, value []T) error {
if len(value) == 0 {
return fmt.Errorf("expected %#v not to be empty", field)
@@ -118,6 +128,13 @@ func NotEmptyString(field string, value string) error {
return nil
}
+func Nil(field string, value any) error {
+ if value != nil && !reflect.ValueOf(value).IsNil() {
+ return fmt.Errorf("expected %s to be nil", field)
+ }
+ return nil
+}
+
func NotNil(field string, value any) error {
if value == nil || reflect.ValueOf(value).IsNil() {
return fmt.Errorf("expected %s not to be nil", field)
diff --git a/test/internal/integration/constants/constants.go b/test/internal/integration/constants/constants.go
index b17328223..d393f041e 100644
--- a/test/internal/integration/constants/constants.go
+++ b/test/internal/integration/constants/constants.go
@@ -25,7 +25,7 @@ import (
const (
Namespace = "default"
- ConsistentTimeout = time.Second * 3
+ ConsistentTimeout = time.Second * 2
EventualTimeout = time.Second * 30
Interval = time.Millisecond * 250
@@ -46,7 +46,7 @@ const (
ApiWithRateLimit = SamplesPath + "/apim/api_definition/v2/api-with-rate-limit.yml"
ApiWithStateStopped = SamplesPath + "/apim/api_definition/v2/api-with-state-stopped.yml"
ApiWithSyncFromAPIM = SamplesPath + "/apim/api_definition/v2/api-with-sync-from-apim.yml"
- ApiWithIds = SamplesPath + "/apim/api_definition/v2/api-with-ids.yml"
+ ApiWithIDs = SamplesPath + "/apim/api_definition/v2/api-with-ids.yml"
ApiWithDisabledPolicy = SamplesPath + "/apim/api_definition/v2/api-with-disabled-policy.yml"
ApiWithTemplatingFile = SamplesPath + "/apim/api_definition/v2/api-with-templating.yml"
ApiWithTemplatingSecretFile = SamplesPath + "/apim/api_definition/v2/api-with-templating-secret.yml"
diff --git a/test/internal/integration/fixture/build.go b/test/internal/integration/fixture/build.go
index 46e77fffc..14dd18694 100644
--- a/test/internal/integration/fixture/build.go
+++ b/test/internal/integration/fixture/build.go
@@ -206,7 +206,7 @@ func randomizeIngressRules(ing *netV1.Ingress, suffix string) {
}
}
-func isTemplate(api custom.ApiDefinition) bool {
+func isTemplate(api custom.ApiDefinitionResource) bool {
return api.GetAnnotations()[keys.IngressTemplateAnnotation] == env.TrueString
}