pkg/webhook/packagebundlecontroller_webhook.go (117 lines of code) (raw):
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// 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"
"fmt"
"net/http"
admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/aws/eks-anywhere-packages/api/v1alpha1"
"github.com/aws/eks-anywhere-packages/pkg/authenticator"
)
type activeBundleValidator struct {
Client client.Client
Config *rest.Config
decoder *admission.Decoder
tcc authenticator.TargetClusterClient
}
func InitPackageBundleControllerValidator(mgr ctrl.Manager) error {
tcc := authenticator.NewTargetClusterClient(mgr.GetLogger(), mgr.GetConfig(), mgr.GetClient())
mgr.GetWebhookServer().
Register("/validate-packages-eks-amazonaws-com-v1alpha1-packagebundlecontroller",
&webhook.Admission{Handler: &activeBundleValidator{
Client: mgr.GetClient(),
Config: mgr.GetConfig(),
tcc: tcc,
decoder: admission.NewDecoder(mgr.GetScheme()),
}})
return nil
}
func (v *activeBundleValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
pbc := &v1alpha1.PackageBundleController{}
err := v.decoder.Decode(req, pbc)
if err != nil {
return admission.Errored(http.StatusInternalServerError,
fmt.Errorf("decoding request: %w", err))
}
bundles := &v1alpha1.PackageBundleList{}
err = v.Client.List(ctx, bundles, &client.ListOptions{Namespace: v1alpha1.PackageNamespace})
if err != nil {
return admission.Errored(http.StatusInternalServerError,
fmt.Errorf("listing package bundles: %w", err))
}
resp, err := v.handleInner(ctx, pbc, bundles)
if err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}
return *resp
}
func (v *activeBundleValidator) handleInner(ctx context.Context, pbc *v1alpha1.PackageBundleController, bundles *v1alpha1.PackageBundleList) (
*admission.Response, error,
) {
if pbc.Spec.ActiveBundle == "" {
resp := &admission.Response{
AdmissionResponse: admissionv1.AdmissionResponse{
Allowed: true,
},
}
return resp, nil
}
var found bool
var theBundle v1alpha1.PackageBundle
for _, b := range bundles.Items {
if b.Name == pbc.Spec.ActiveBundle {
theBundle = b
found = true
break
}
}
if !found {
reason := fmt.Sprintf("activeBundle %q not present on cluster", pbc.Spec.ActiveBundle)
resp := &admission.Response{
AdmissionResponse: admissionv1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusBadRequest,
Message: reason,
Reason: metav1.StatusReason(reason),
},
},
}
return resp, nil
}
info, err := v.tcc.GetServerVersion(ctx, pbc.Name)
if err != nil {
return nil, fmt.Errorf("getting server version for %s: %w", pbc.Name, err)
}
matches, err := theBundle.KubeVersionMatches(info)
if err != nil {
return nil, fmt.Errorf("listing package bundles: %w", err)
}
if !matches {
reason := fmt.Sprintf("kuberneetes version v%s-%s does not match %s", info.Major, info.Minor, pbc.Spec.ActiveBundle)
resp := &admission.Response{
AdmissionResponse: admissionv1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusBadRequest,
Message: reason,
Reason: metav1.StatusReason(reason),
},
},
}
return resp, nil
}
resp := &admission.Response{
AdmissionResponse: admissionv1.AdmissionResponse{
Allowed: true,
},
}
return resp, nil
}