openstack-operator/controllers/designate_controller.go

193 lines
6.9 KiB
Go

// Copyright 2020 VEXXHOST, Inc.
//
// 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 controllers
import (
"context"
"fmt"
"strings"
"time"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
dnsv1 "opendev.org/vexxhost/openstack-operator/api/dns/v1"
"opendev.org/vexxhost/openstack-operator/builders"
"opendev.org/vexxhost/openstack-operator/utils/baseutils"
"opendev.org/vexxhost/openstack-operator/utils/k8sutils"
"opendev.org/vexxhost/openstack-operator/utils/openstackutils"
)
// DesignateReconciler reconciles a Designate object
type DesignateReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
DesignateClient *openstackutils.DesignateClientBuilder
}
const (
_autoReconcilePeriod = 15 * time.Second
_designatingAnnotation = "dns.openstack.org/designate"
_defaultDesignatingAnnotation = "dns.openstack.org/is-default-designate"
)
// +kubebuilder:rbac:groups=dns.openstack.org,resources=designates,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=dns.openstack.org,resources=designates/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=dns.openstack.org,resources=zones,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=dns.openstack.org,resources=zones/status,verbs=get;update;patch
// Reconcile does the reconcilication of designate instances
func (r *DesignateReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
var (
credentials corev1.Secret
designate dnsv1.Designate
)
ctx := context.Background()
log := r.Log.WithValues("Designate", req.NamespacedName)
labels := map[string]string{
"app.kubernetes.io/name": "designate",
"app.kubernetes.io/managed-by": "openstack-operator",
}
// 1 Get designate instance
if err := r.Get(ctx, req.NamespacedName, &designate); err != nil {
log.Error(err, "unable to fetch designate "+req.Name+":"+req.Namespace)
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 2 Get credentials
if err := r.Get(ctx, types.NamespacedName{
Namespace: req.Namespace,
Name: designate.Spec.Credentials,
}, &credentials); err != nil {
log.Error(err, "unable to fetch rc secret "+designate.Spec.Credentials+":"+req.Namespace)
return ctrl.Result{}, err
}
credential, ok := credentials.Data["clouds.yaml"]
if !ok {
err := fmt.Errorf("rc secret syntax error ")
log.Error(err, designate.Spec.Credentials+":"+designate.Spec.CloudName)
return ctrl.Result{}, err
}
// 3 Get designate client
if err := openstackutils.DesignateClient(r.DesignateClient, credential, designate.Spec.CloudName); err != nil {
log.WithValues("resource", "designateClient").WithValues("op", "op").Info("ClientCreationFailed" + err.Error())
return ctrl.Result{}, err
}
// 4 Create zone CRs
// 4-1 Get zone list from the designate
desinateZoneSpeclist := map[string]dnsv1.ZoneSpec{}
desinateZoneNameList := []string{}
designateZones, err := r.DesignateClient.ListZone()
if err != nil {
log.WithValues("resource", "Zone").WithValues("op", "op").Info("Error: Get zone list in the designate" + err.Error())
return ctrl.Result{}, err
}
for _, zone := range designateZones {
desinateZoneNameList = append(desinateZoneNameList, zone.Name)
desinateZoneSpeclist[zone.Name] = dnsv1.ZoneSpec{
Domain: zone.Name,
Email: zone.Email,
TTL: zone.TTL,
}
}
log.Info("Get Zone list in the Designate")
log.Info("Zone list in the Designate" + fmt.Sprintf("%v", desinateZoneSpeclist))
// 4-2 Get zone list in the cluster
clusterZoneObjectMetalist := map[string]metav1.ObjectMeta{}
clusterZoneNameList := []string{}
clusterZones := &dnsv1.ZoneList{}
if err := r.List(context.Background(), clusterZones); err != nil {
log.WithValues("resource", "Zone").WithValues("op", "op").Info("Error: Get zone list in the cluster" + err.Error())
return ctrl.Result{}, err
}
for _, zone := range clusterZones.Items {
clusterZoneNameList = append(clusterZoneNameList, zone.Spec.Domain)
clusterZoneObjectMetalist[zone.Spec.Domain] = metav1.ObjectMeta{
Name: zone.Name,
Namespace: zone.Namespace,
}
}
log.Info("Zone list in the cluster" + fmt.Sprintf("%v", clusterZoneNameList))
clusterOnlyNameList, designateOnlyNameList := baseutils.CompareStrSlice(clusterZoneNameList, desinateZoneNameList)
log.Info("Zone list in the only cluster" + fmt.Sprintf("%v", clusterOnlyNameList))
log.Info("Zone list in the only designate" + fmt.Sprintf("%v", designateOnlyNameList))
// 4-3 Create zone list (designateOnlyNameList) in the cluster
log.Info("Create Zone list in the cluster")
for _, zoneName := range designateOnlyNameList {
Zone := &dnsv1.Zone{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: strings.ReplaceAll(zoneName[:len(zoneName)-1], ".", "-"),
},
}
op, err := k8sutils.CreateOrUpdate(ctx, r, Zone, func() error {
return builders.Zone(Zone, &designate, r.Scheme).
Labels(labels).
Annotation(_designatingAnnotation, req.Name).
Domain(zoneName).
TTL(desinateZoneSpeclist[zoneName].TTL).
Email(desinateZoneSpeclist[zoneName].Email).
Build()
})
if err != nil {
return ctrl.Result{}, err
}
log.WithValues("resource", "Zone").WithValues("op", op).Info("Reconciled")
// err = r.Create(context.Background(), Zone)
// if err != nil {
// log.WithValues("resource", "Zone").WithValues("op", "op").Info("ZoneCreationFailed on Cluster -" + zoneName + ":" + err.Error())
// return ctrl.Result{}, err
// }
}
// 4-4 Delete zone list (clusterOnlyNameList) in the cluster
log.Info("Delete Zone list in the cluster")
for _, zoneName := range clusterOnlyNameList {
Zone := &dnsv1.Zone{
ObjectMeta: clusterZoneObjectMetalist[zoneName],
}
err = r.Delete(context.Background(), Zone)
if err != nil {
log.WithValues("resource", "Zone").WithValues("op", "op").Info("ZoneCreationFailed on Cluster -" + zoneName + ":" + err.Error())
return ctrl.Result{}, err
}
}
return ctrl.Result{Requeue: true, RequeueAfter: _autoReconcilePeriod}, nil
}
// SetupWithManager initializes the controller with primary manager
func (r *DesignateReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&dnsv1.Designate{}).
Owns(&dnsv1.Zone{}).
Complete(r)
}