appconfigmgrv2/controllers/network_policies.go (131 lines of code) (raw):

// Copyright 2019 Google LLC // // 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. // // Copyright 2019 Google LLC. This software is provided as-is, // without warranty or representation for any use or purpose. // package controllers import ( "context" "fmt" "reflect" appconfig "github.com/GoogleCloudPlatform/anthos-appconfig/appconfigmgrv2/api/v1alpha1" netv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) // reconcileNetworkPolicies reconciles kubernetes NetworkPolicy resources to enforce // transport-level allowedClients support in the absence of istio. func (r *AppEnvConfigTemplateV2Reconciler) reconcileNetworkPolicies( ctx context.Context, in *appconfig.AppEnvConfigTemplateV2, ) error { names := make(map[types.NamespacedName]bool) nps, err := networkPolicies(in) if err != nil { return fmt.Errorf("building policies: %v", err) } for _, np := range nps { if err := controllerutil.SetControllerReference(in, np, r.Scheme); err != nil { return fmt.Errorf("setting controller reference: %v", err) } if err := r.reconcileNetworkPolicy(ctx, np); err != nil { return fmt.Errorf("reconciling: %v", err) } names[types.NamespacedName{Name: np.Name, Namespace: np.Namespace}] = true } if err := r.garbageCollectNetworkPolicies(ctx, in, names); err != nil { return fmt.Errorf("garbage collecting: %v", err) } return nil } func (r *AppEnvConfigTemplateV2Reconciler) reconcileNetworkPolicy( ctx context.Context, desired *netv1.NetworkPolicy, ) error { found := &netv1.NetworkPolicy{} err := r.Get(ctx, types.NamespacedName{ Name: desired.Name, Namespace: desired.Namespace, }, found) if err != nil { if errors.IsNotFound(err) { log.Info("Creating", "resource", "networkpolicies", "namespace", found.Namespace, "name", found.Name) if err := r.Create(ctx, desired); err != nil { return fmt.Errorf("creating: %v", err) } return nil } else { return fmt.Errorf("getting: %v", err) } } if !reflect.DeepEqual(desired.Spec, found.Spec) { found.Spec = desired.Spec log.Info("Updating", "resource", "networkpolicies", "namespace", found.Namespace, "name", found.Name) if err := r.Update(ctx, found); err != nil { return fmt.Errorf("updating: %v", err) } } return nil } func (r *AppEnvConfigTemplateV2Reconciler) garbageCollectNetworkPolicies( ctx context.Context, in *appconfig.AppEnvConfigTemplateV2, names map[types.NamespacedName]bool, ) error { var list netv1.NetworkPolicyList if err := r.List(ctx, &list, func(opt *client.ListOptions) {}); err != nil { return fmt.Errorf("listing: %v", err) } for _, np := range list.Items { if !metav1.IsControlledBy(&np, in) { continue } if !names[types.NamespacedName{Name: np.Name, Namespace: np.Namespace}] { log.Info("Deleting", "resource", "networkpolicies", "namespace", np.Namespace, "name", np.Name) if err := r.Delete(ctx, &np); err != nil { return fmt.Errorf("deleting: %v", err) } } } return nil } func networkPolicies(in *appconfig.AppEnvConfigTemplateV2) ([]*netv1.NetworkPolicy, error) { var ps []*netv1.NetworkPolicy for i := range in.Spec.Services { if len(in.Spec.Services[i].AllowedClients) == 0 { continue } clients := make([]netv1.NetworkPolicyPeer, 0) for _, c := range in.Spec.Services[i].AllowedClients { ns, app, err := parseAllowedClient(c.Name, in.Namespace) if err != nil { return nil, fmt.Errorf("parsing allowed client: %v", err) } // TODO: What to do with namespace? You can only select namespaces by labels, // not name. _ = ns clients = append(clients, netv1.NetworkPolicyPeer{ NamespaceSelector: nil, PodSelector: &metav1.LabelSelector{ MatchLabels: map[string]string{"app": app}, }, }) } ps = append(ps, &netv1.NetworkPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: networkPolicyName(in, i), Namespace: in.Namespace, }, Spec: netv1.NetworkPolicySpec{ PodSelector: metav1.LabelSelector{ MatchLabels: map[string]string{ "app": in.Spec.Services[i].DeploymentApp, }, }, Ingress: []netv1.NetworkPolicyIngressRule{ { From: clients, }, }, }, }) } return ps, nil } func networkPolicyName(in *appconfig.AppEnvConfigTemplateV2, i int) string { return fmt.Sprintf("%v-service--%v", in.Name, in.Spec.Services[i].Name) }