From 490dc4a9bb77fd08b911d48a5594ee5396d387ab Mon Sep 17 00:00:00 2001 From: Douglas Mayle Date: Tue, 26 May 2020 18:29:04 +0200 Subject: [PATCH] Add flag for go template to format host names When using an alternate DNS like external-dns, your host names will be different than the internal core-dns format. This adds a flag so that the user can specify the name format using a go template. --- main.go | 11 +++-- pkg/apis/operator.min.io/v1/helper.go | 40 +++++++++++++++++++ pkg/controller/cluster/main-controller.go | 13 ++++-- .../statefulsets/minio-statefulset.go | 9 +++-- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/main.go b/main.go index fdfe284f3ea..7a41eeaaf10 100644 --- a/main.go +++ b/main.go @@ -45,9 +45,10 @@ import ( var Version = "DEVELOPMENT.GOGET" var ( - masterURL string - kubeconfig string - checkVersion bool + masterURL string + kubeconfig string + hostsTemplate string + checkVersion bool onlyOneSignalHandler = make(chan struct{}) shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM} @@ -56,6 +57,7 @@ var ( func init() { flag.StringVar(&kubeconfig, "kubeconfig", "", "path to a kubeconfig. Only required if out-of-cluster") flag.StringVar(&masterURL, "master", "", "the address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster") + flag.StringVar(&hostsTemplate, "hosts-template", "", "the go template to use for hostname formatting of name fields (StatefulSet, CIService, HLService, Ellipsis, Domain)") flag.BoolVar(&checkVersion, "version", false, "print version") } @@ -117,7 +119,8 @@ func main() { kubeInformerFactory.Apps().V1().Deployments(), kubeInformerFactory.Batch().V1().Jobs(), minioInformerFactory.Operator().V1().MinIOInstances(), - kubeInformerFactory.Core().V1().Services()) + kubeInformerFactory.Core().V1().Services(), + hostsTemplate) mirrorController := mirror.NewController(kubeClient, controllerClient, kubeInformerFactory.Batch().V1().Jobs(), diff --git a/pkg/apis/operator.min.io/v1/helper.go b/pkg/apis/operator.min.io/v1/helper.go index c33e1997c33..d612dbfe2d8 100644 --- a/pkg/apis/operator.min.io/v1/helper.go +++ b/pkg/apis/operator.min.io/v1/helper.go @@ -18,6 +18,7 @@ package v1 import ( + "bytes" "context" "crypto/tls" "errors" @@ -26,6 +27,7 @@ import ( "net/http" "path" "strconv" + "text/template" "time" appsv1 "k8s.io/api/apps/v1" @@ -39,6 +41,14 @@ import ( "github.com/minio/minio/pkg/madmin" ) +type hostsTemplateValues struct { + StatefulSet string + CIService string + HLService string + Ellipsis string + Domain string +} + // HasCredsSecret returns true if the user has provided a secret // for a MinIOInstance else false func (mi *MinIOInstance) HasCredsSecret() bool { @@ -203,6 +213,36 @@ func (mi *MinIOInstance) MinIOHosts() []string { return hosts } +// TemplatedMinIOHosts returns the domain names in ellipses format created for current MinIOInstance without the service part +func (mi *MinIOInstance) TemplatedMinIOHosts(hostsTemplate string) []string { + hosts := make([]string, 0) + tmpl, err := template.New("hosts").Parse(hostsTemplate) + if err != nil { + msg := "Invalid go template for hosts" + klog.V(2).Infof(msg) + return hosts + } + var max, index int32 + // Create the ellipses style URL + for _, z := range mi.Spec.Zones { + max = max + z.Servers + data := hostsTemplateValues{ + StatefulSet: mi.MinIOStatefulSetName(), + CIService: mi.MinIOCIServiceName(), + HLService: mi.MinIOHLServiceName(), + Ellipsis: "{" + strconv.Itoa(int(index)) + "..." + strconv.Itoa(int(max)-1) + "}", + Domain: ClusterDomain, + } + output := new(bytes.Buffer) + if err = tmpl.Execute(output, data); err != nil { + continue + } + hosts = append(hosts, output.String()) + index = max + } + return hosts +} + // AllMinIOHosts returns the all the individual domain names relevant for current MinIOInstance func (mi *MinIOInstance) AllMinIOHosts() []string { hosts := make([]string, 0) diff --git a/pkg/controller/cluster/main-controller.go b/pkg/controller/cluster/main-controller.go index e6d455a7aa9..41e295787be 100644 --- a/pkg/controller/cluster/main-controller.go +++ b/pkg/controller/cluster/main-controller.go @@ -125,6 +125,9 @@ type Controller struct { // recorder is an event recorder for recording Event resources to the // Kubernetes API. recorder record.EventRecorder + + // Use a go template to render the hosts string + hostsTemplate string } // NewController returns a new sample controller @@ -136,7 +139,8 @@ func NewController( deploymentInformer appsinformers.DeploymentInformer, jobInformer batchinformers.JobInformer, minioInstanceInformer informers.MinIOInstanceInformer, - serviceInformer coreinformers.ServiceInformer) *Controller { + serviceInformer coreinformers.ServiceInformer, + hostsTemplate string) *Controller { // Create event broadcaster // Add minio-controller types to the default Kubernetes Scheme so Events can be @@ -164,6 +168,7 @@ func NewController( serviceListerSynced: serviceInformer.Informer().HasSynced, workqueue: queue.NewNamedRateLimitingQueue(queue.DefaultControllerRateLimiter(), "MinIOInstances"), recorder: recorder, + hostsTemplate: hostsTemplate, } klog.Info("Setting up event handlers") @@ -411,7 +416,7 @@ func (c *Controller) syncHandler(key string) error { if err != nil { return err } - ss = statefulsets.NewForMinIO(mi, hlSvc.Name) + ss = statefulsets.NewForMinIO(mi, hlSvc.Name, c.hostsTemplate) ss, err = c.kubeClientSet.AppsV1().StatefulSets(mi.Namespace).Create(ctx, ss, cOpts) if err != nil { return err @@ -446,7 +451,7 @@ func (c *Controller) syncHandler(key string) error { } } - ss = statefulsets.NewForMinIO(mi, hlSvc.Name) + ss = statefulsets.NewForMinIO(mi, hlSvc.Name, c.hostsTemplate) klog.V(2).Infof("Removing the existing StatefulSet %s with replicas: %d", name, *ss.Spec.Replicas) if err := c.kubeClientSet.AppsV1().StatefulSets(mi.Namespace).Delete(ctx, ss.Name, metav1.DeleteOptions{}); err != nil { return err @@ -472,7 +477,7 @@ func (c *Controller) syncHandler(key string) error { return err } klog.V(4).Infof("Updating MinIOInstance %s MinIO server version %s, to: %s", name, mi.Spec.Image, ss.Spec.Template.Spec.Containers[0].Image) - ss = statefulsets.NewForMinIO(mi, hlSvc.Name) + ss = statefulsets.NewForMinIO(mi, hlSvc.Name, c.hostsTemplate) if _, err := c.kubeClientSet.AppsV1().StatefulSets(mi.Namespace).Update(ctx, ss, uOpts); err != nil { return err } diff --git a/pkg/resources/statefulsets/minio-statefulset.go b/pkg/resources/statefulsets/minio-statefulset.go index 1430685e830..99dcc643b6b 100644 --- a/pkg/resources/statefulsets/minio-statefulset.go +++ b/pkg/resources/statefulsets/minio-statefulset.go @@ -150,7 +150,7 @@ func volumeMounts(mi *miniov1.MinIOInstance) []corev1.VolumeMount { } // Builds the MinIO container for a MinIOInstance. -func minioServerContainer(mi *miniov1.MinIOInstance, serviceName string) corev1.Container { +func minioServerContainer(mi *miniov1.MinIOInstance, serviceName string, hostsTemplate string) corev1.Container { args := []string{"server"} if mi.Spec.Zones[0].Servers == 1 { @@ -159,6 +159,9 @@ func minioServerContainer(mi *miniov1.MinIOInstance, serviceName string) corev1. } else { // append all the MinIOInstance replica URLs hosts := mi.MinIOHosts() + if hostsTemplate != "" { + hosts = mi.TemplatedMinIOHosts(hostsTemplate) + } for _, h := range hosts { args = append(args, fmt.Sprintf("%s://"+h+"%s", miniov1.Scheme, mi.VolumePath())) } @@ -217,7 +220,7 @@ func getVolumesForContainer(mi *miniov1.MinIOInstance) []corev1.Volume { } // NewForMinIO creates a new StatefulSet for the given Cluster. -func NewForMinIO(mi *miniov1.MinIOInstance, serviceName string) *appsv1.StatefulSet { +func NewForMinIO(mi *miniov1.MinIOInstance, serviceName string, hostsTemplate string) *appsv1.StatefulSet { // If a PV isn't specified just use a EmptyDir volume var podVolumes = getVolumesForContainer(mi) var replicas = mi.MinIOReplicas() @@ -299,7 +302,7 @@ func NewForMinIO(mi *miniov1.MinIOInstance, serviceName string) *appsv1.Stateful }) } - containers := []corev1.Container{minioServerContainer(mi, serviceName)} + containers := []corev1.Container{minioServerContainer(mi, serviceName, hostsTemplate)} ss := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{