139 lines
4.2 KiB
Go
139 lines
4.2 KiB
Go
|
package controllers
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
seaweedv1 "github.com/seaweedfs/seaweedfs-operator/api/v1"
|
||
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
|
"k8s.io/apimachinery/pkg/runtime"
|
||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||
|
"k8s.io/client-go/kubernetes/scheme"
|
||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||
|
)
|
||
|
|
||
|
// svcName is the backend service name
|
||
|
func createIngress(seaweedCR *seaweedv1.Seaweed, svcName string, port int) *extensionsv1beta1.Ingress {
|
||
|
ingressLabel := map[string]string{"app": "seaweedfs", "role": "ingress", "name": svcName}
|
||
|
|
||
|
ingress := &extensionsv1beta1.Ingress{
|
||
|
ObjectMeta: metav1.ObjectMeta{
|
||
|
Name: svcName + "-ingress",
|
||
|
Namespace: seaweedCR.Namespace,
|
||
|
Labels: ingressLabel,
|
||
|
},
|
||
|
Spec: extensionsv1beta1.IngressSpec{
|
||
|
Rules: []extensionsv1beta1.IngressRule{},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, host := range seaweedCR.Spec.Hosts {
|
||
|
rule := extensionsv1beta1.IngressRule{
|
||
|
Host: host,
|
||
|
IngressRuleValue: extensionsv1beta1.IngressRuleValue{
|
||
|
HTTP: &extensionsv1beta1.HTTPIngressRuleValue{
|
||
|
Paths: []extensionsv1beta1.HTTPIngressPath{
|
||
|
{
|
||
|
Path: "/",
|
||
|
Backend: extensionsv1beta1.IngressBackend{
|
||
|
ServiceName: svcName,
|
||
|
ServicePort: intstr.FromInt(port),
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
ingress.Spec.Rules = append(ingress.Spec.Rules, rule)
|
||
|
}
|
||
|
return ingress
|
||
|
}
|
||
|
|
||
|
// the following is adapted from tidb-operator/pkg/controller/generic_control.go
|
||
|
|
||
|
type MergeFn func(existing, desired runtime.Object) error
|
||
|
|
||
|
// CreateOrUpdate create an object to the Kubernetes cluster for controller, if the object to create is existed,
|
||
|
// call mergeFn to merge the change in new object to the existing object, then update the existing object.
|
||
|
// The object will also be adopted by the given controller.
|
||
|
func (r *SeaweedReconciler) CreateOrUpdate(controller, obj runtime.Object, mergeFn MergeFn) (runtime.Object, error) {
|
||
|
|
||
|
// controller-runtime/client will mutate the object pointer in-place,
|
||
|
// to be consistent with other methods in our controller, we copy the object
|
||
|
// to avoid the in-place mutation here and hereafter.
|
||
|
desired := obj.DeepCopyObject()
|
||
|
|
||
|
// 1. try to create and see if there is any conflicts
|
||
|
err := r.Create(context.TODO(), desired)
|
||
|
if errors.IsAlreadyExists(err) {
|
||
|
|
||
|
// 2. object has already existed, merge our desired changes to it
|
||
|
existing, err := EmptyClone(obj)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
key, err := client.ObjectKeyFromObject(existing)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
err = r.Get(context.TODO(), key, existing)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
mutated := existing.DeepCopyObject()
|
||
|
// 4. invoke mergeFn to mutate a copy of the existing object
|
||
|
if err := mergeFn(mutated, desired); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// 5. check if the copy is actually mutated
|
||
|
if !apiequality.Semantic.DeepEqual(existing, mutated) {
|
||
|
err := r.Update(context.TODO(), mutated)
|
||
|
return mutated, err
|
||
|
}
|
||
|
|
||
|
return mutated, nil
|
||
|
}
|
||
|
|
||
|
return desired, err
|
||
|
}
|
||
|
|
||
|
// EmptyClone create an clone of the resource with the same name and namespace (if namespace-scoped), with other fields unset
|
||
|
func EmptyClone(obj runtime.Object) (runtime.Object, error) {
|
||
|
meta, ok := obj.(metav1.Object)
|
||
|
if !ok {
|
||
|
return nil, fmt.Errorf("Obj %v is not a metav1.Object, cannot call EmptyClone", obj)
|
||
|
}
|
||
|
gvk, err := InferObjectKind(obj)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
inst, err := scheme.Scheme.New(gvk)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
instMeta, ok := inst.(metav1.Object)
|
||
|
if !ok {
|
||
|
return nil, fmt.Errorf("New instatnce %v created from scheme is not a metav1.Object, EmptyClone failed", inst)
|
||
|
}
|
||
|
instMeta.SetName(meta.GetName())
|
||
|
instMeta.SetNamespace(meta.GetNamespace())
|
||
|
return inst, nil
|
||
|
}
|
||
|
|
||
|
// InferObjectKind infers the object kind
|
||
|
func InferObjectKind(obj runtime.Object) (schema.GroupVersionKind, error) {
|
||
|
gvks, _, err := scheme.Scheme.ObjectKinds(obj)
|
||
|
if err != nil {
|
||
|
return schema.GroupVersionKind{}, err
|
||
|
}
|
||
|
if len(gvks) != 1 {
|
||
|
return schema.GroupVersionKind{}, fmt.Errorf("Object %v has ambigious GVK", obj)
|
||
|
}
|
||
|
return gvks[0], nil
|
||
|
}
|