Add Zone and Designate CRs
Change-Id: I7b59fc5ae66adb4d123c10249e2321c6bc6537c5
This commit is contained in:
39
api/dns/v1/designate_type.go
Normal file
39
api/dns/v1/designate_type.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DesignateSpec defines the desired state of Designate
|
||||||
|
type DesignateSpec struct {
|
||||||
|
Credentials string `json:"credentials"`
|
||||||
|
CloudName string `json:"cloudname"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DesignateStatus defines the observed state of Designate
|
||||||
|
type DesignateStatus struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// Designate is the Schema for the Designates API
|
||||||
|
type Designate struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec DesignateSpec `json:"spec,omitempty"`
|
||||||
|
Status DesignateStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// DesignateList contains a list of Designate
|
||||||
|
type DesignateList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []Designate `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&Designate{}, &DesignateList{})
|
||||||
|
}
|
||||||
20
api/dns/v1/groupversion_info.go
Normal file
20
api/dns/v1/groupversion_info.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Package v1 contains API Schema definitions for the infrastructure v1 API group
|
||||||
|
// +kubebuilder:object:generate=true
|
||||||
|
// +groupName=dns.openstack.org
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// GroupVersion is group version used to register these objects
|
||||||
|
GroupVersion = schema.GroupVersion{Group: "dns.openstack.org", Version: "v1"}
|
||||||
|
|
||||||
|
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||||
|
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||||
|
|
||||||
|
// AddToScheme adds the types in this group-version to the given scheme.
|
||||||
|
AddToScheme = SchemeBuilder.AddToScheme
|
||||||
|
)
|
||||||
44
api/dns/v1/zone_type.go
Normal file
44
api/dns/v1/zone_type.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ZoneSpec defines the desired state of Zone
|
||||||
|
type ZoneSpec struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
TTL int `json:"ttl"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneStatus defines the observed state of Zone
|
||||||
|
type ZoneStatus struct {
|
||||||
|
// +kubebuilder:validation:Default=Pending
|
||||||
|
ZoneID string `json:"zoneId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// Zone is the Schema for the Zones API
|
||||||
|
type Zone struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec ZoneSpec `json:"spec,omitempty"`
|
||||||
|
Status ZoneStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// ZoneList contains a list of Zone
|
||||||
|
type ZoneList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []Zone `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&Zone{}, &ZoneList{})
|
||||||
|
}
|
||||||
203
api/dns/v1/zz_generated.deepcopy.go
Normal file
203
api/dns/v1/zz_generated.deepcopy.go
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by controller-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Designate) DeepCopyInto(out *Designate) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
out.Spec = in.Spec
|
||||||
|
out.Status = in.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Designate.
|
||||||
|
func (in *Designate) DeepCopy() *Designate {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Designate)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *Designate) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *DesignateList) DeepCopyInto(out *DesignateList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]Designate, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DesignateList.
|
||||||
|
func (in *DesignateList) DeepCopy() *DesignateList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(DesignateList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *DesignateList) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *DesignateSpec) DeepCopyInto(out *DesignateSpec) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DesignateSpec.
|
||||||
|
func (in *DesignateSpec) DeepCopy() *DesignateSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(DesignateSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *DesignateStatus) DeepCopyInto(out *DesignateStatus) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DesignateStatus.
|
||||||
|
func (in *DesignateStatus) DeepCopy() *DesignateStatus {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(DesignateStatus)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Zone) DeepCopyInto(out *Zone) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
out.Spec = in.Spec
|
||||||
|
out.Status = in.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Zone.
|
||||||
|
func (in *Zone) DeepCopy() *Zone {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Zone)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *Zone) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ZoneList) DeepCopyInto(out *ZoneList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]Zone, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneList.
|
||||||
|
func (in *ZoneList) DeepCopy() *ZoneList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ZoneList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *ZoneList) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ZoneSpec) DeepCopyInto(out *ZoneSpec) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneSpec.
|
||||||
|
func (in *ZoneSpec) DeepCopy() *ZoneSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ZoneSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ZoneStatus) DeepCopyInto(out *ZoneStatus) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneStatus.
|
||||||
|
func (in *ZoneStatus) DeepCopy() *ZoneStatus {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ZoneStatus)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
61
builders/zone.go
Normal file
61
builders/zone.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package builders
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
dnsv1 "opendev.org/vexxhost/openstack-operator/api/dns/v1"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ZoneBuilder defines the interface to build a Zone
|
||||||
|
type ZoneBuilder struct {
|
||||||
|
obj *dnsv1.Zone
|
||||||
|
owner metav1.Object
|
||||||
|
scheme *runtime.Scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone returns a new service builder
|
||||||
|
func Zone(existing *dnsv1.Zone, owner metav1.Object, scheme *runtime.Scheme) *ZoneBuilder {
|
||||||
|
existing.Annotations = map[string]string{}
|
||||||
|
return &ZoneBuilder{
|
||||||
|
obj: existing,
|
||||||
|
owner: owner,
|
||||||
|
scheme: scheme,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annotation sets one set annotation
|
||||||
|
func (z *ZoneBuilder) Annotation(key, value string) *ZoneBuilder {
|
||||||
|
z.obj.Annotations[key] = value
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
// Labels specifies labels for the Zone
|
||||||
|
func (z *ZoneBuilder) Labels(labels map[string]string) *ZoneBuilder {
|
||||||
|
z.obj.ObjectMeta.Labels = labels
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain sets Domain for the Zone
|
||||||
|
func (z *ZoneBuilder) Domain(domain string) *ZoneBuilder {
|
||||||
|
z.obj.Spec.Domain = domain
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
// TTL sets TTL for the Zone
|
||||||
|
func (z *ZoneBuilder) TTL(ttl int) *ZoneBuilder {
|
||||||
|
z.obj.Spec.TTL = ttl
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email sets TTL for the Email
|
||||||
|
func (z *ZoneBuilder) Email(email string) *ZoneBuilder {
|
||||||
|
z.obj.Spec.Email = email
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build returns a complete Zone object
|
||||||
|
func (z *ZoneBuilder) Build() error {
|
||||||
|
return controllerutil.SetControllerReference(z.owner, z.obj, z.scheme)
|
||||||
|
}
|
||||||
59
chart/crds/dns.openstack.org_designates.yaml
Normal file
59
chart/crds/dns.openstack.org_designates.yaml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.2.5
|
||||||
|
creationTimestamp: null
|
||||||
|
name: designates.dns.openstack.org
|
||||||
|
spec:
|
||||||
|
group: dns.openstack.org
|
||||||
|
names:
|
||||||
|
kind: Designate
|
||||||
|
listKind: DesignateList
|
||||||
|
plural: designates
|
||||||
|
singular: designate
|
||||||
|
scope: Namespaced
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Designate is the Schema for the Designates API
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
of an object. Servers should convert recognized schemas to the latest
|
||||||
|
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: 'Kind is a string value representing the REST resource this
|
||||||
|
object represents. Servers may infer this from the endpoint the client
|
||||||
|
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
spec:
|
||||||
|
description: DesignateSpec defines the desired state of Designate
|
||||||
|
properties:
|
||||||
|
cloudname:
|
||||||
|
type: string
|
||||||
|
credentials:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- cloudname
|
||||||
|
- credentials
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: DesignateStatus defines the observed state of Designate
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
version: v1
|
||||||
|
versions:
|
||||||
|
- name: v1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
71
chart/crds/dns.openstack.org_zones.yaml
Normal file
71
chart/crds/dns.openstack.org_zones.yaml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.2.5
|
||||||
|
creationTimestamp: null
|
||||||
|
name: zones.dns.openstack.org
|
||||||
|
spec:
|
||||||
|
group: dns.openstack.org
|
||||||
|
names:
|
||||||
|
kind: Zone
|
||||||
|
listKind: ZoneList
|
||||||
|
plural: zones
|
||||||
|
singular: zone
|
||||||
|
scope: Namespaced
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Zone is the Schema for the Zones API
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
of an object. Servers should convert recognized schemas to the latest
|
||||||
|
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: 'Kind is a string value representing the REST resource this
|
||||||
|
object represents. Servers may infer this from the endpoint the client
|
||||||
|
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
spec:
|
||||||
|
description: ZoneSpec defines the desired state of Zone
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
domain:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
ttl:
|
||||||
|
type: integer
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- domain
|
||||||
|
- email
|
||||||
|
- ttl
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: ZoneStatus defines the observed state of Zone
|
||||||
|
properties:
|
||||||
|
zoneId:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- zoneId
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
version: v1
|
||||||
|
versions:
|
||||||
|
- name: v1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
@@ -43,6 +43,18 @@ rules:
|
|||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
- watch
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
@@ -56,6 +68,46 @@ rules:
|
|||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
- watch
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- dns.openstack.org
|
||||||
|
resources:
|
||||||
|
- designates
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- dns.openstack.org
|
||||||
|
resources:
|
||||||
|
- designates/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- dns.openstack.org
|
||||||
|
resources:
|
||||||
|
- zones
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- dns.openstack.org
|
||||||
|
resources:
|
||||||
|
- zones/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- infrastructure.vexxhost.cloud
|
- infrastructure.vexxhost.cloud
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
@@ -7,3 +7,9 @@
|
|||||||
{{ $.Files.Get $path }}
|
{{ $.Files.Get $path }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- if .Values.crd.dns }}
|
||||||
|
{{- range $path, $bytes := .Files.Glob "crds/dns.openstack.org*.yaml" }}
|
||||||
|
{{ $.Files.Get $path }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end -}}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
crd:
|
crd:
|
||||||
monitoring: true
|
monitoring: true
|
||||||
|
dns: true
|
||||||
|
|||||||
59
config/crd/bases/dns.openstack.org_designates.yaml
Normal file
59
config/crd/bases/dns.openstack.org_designates.yaml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.2.5
|
||||||
|
creationTimestamp: null
|
||||||
|
name: designates.dns.openstack.org
|
||||||
|
spec:
|
||||||
|
group: dns.openstack.org
|
||||||
|
names:
|
||||||
|
kind: Designate
|
||||||
|
listKind: DesignateList
|
||||||
|
plural: designates
|
||||||
|
singular: designate
|
||||||
|
scope: Namespaced
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Designate is the Schema for the Designates API
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
of an object. Servers should convert recognized schemas to the latest
|
||||||
|
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: 'Kind is a string value representing the REST resource this
|
||||||
|
object represents. Servers may infer this from the endpoint the client
|
||||||
|
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
spec:
|
||||||
|
description: DesignateSpec defines the desired state of Designate
|
||||||
|
properties:
|
||||||
|
cloudname:
|
||||||
|
type: string
|
||||||
|
credentials:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- cloudname
|
||||||
|
- credentials
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: DesignateStatus defines the observed state of Designate
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
version: v1
|
||||||
|
versions:
|
||||||
|
- name: v1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
71
config/crd/bases/dns.openstack.org_zones.yaml
Normal file
71
config/crd/bases/dns.openstack.org_zones.yaml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.2.5
|
||||||
|
creationTimestamp: null
|
||||||
|
name: zones.dns.openstack.org
|
||||||
|
spec:
|
||||||
|
group: dns.openstack.org
|
||||||
|
names:
|
||||||
|
kind: Zone
|
||||||
|
listKind: ZoneList
|
||||||
|
plural: zones
|
||||||
|
singular: zone
|
||||||
|
scope: Namespaced
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Zone is the Schema for the Zones API
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
of an object. Servers should convert recognized schemas to the latest
|
||||||
|
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: 'Kind is a string value representing the REST resource this
|
||||||
|
object represents. Servers may infer this from the endpoint the client
|
||||||
|
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
spec:
|
||||||
|
description: ZoneSpec defines the desired state of Zone
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
domain:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
ttl:
|
||||||
|
type: integer
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- domain
|
||||||
|
- email
|
||||||
|
- ttl
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: ZoneStatus defines the observed state of Zone
|
||||||
|
properties:
|
||||||
|
zoneId:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- zoneId
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
version: v1
|
||||||
|
versions:
|
||||||
|
- name: v1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
@@ -7,6 +7,8 @@ resources:
|
|||||||
- bases/infrastructure.vexxhost.cloud_rabbitmqs.yaml
|
- bases/infrastructure.vexxhost.cloud_rabbitmqs.yaml
|
||||||
- bases/monitoring.coreos.com_podmonitors.yaml
|
- bases/monitoring.coreos.com_podmonitors.yaml
|
||||||
- bases/monitoring.coreos.com_prometheusrules.yaml
|
- bases/monitoring.coreos.com_prometheusrules.yaml
|
||||||
|
- bases/dns.openstack.org_zones.yaml
|
||||||
|
- bases/dns.openstack.org_designates.yaml
|
||||||
# +kubebuilder:scaffold:crdkustomizeresource
|
# +kubebuilder:scaffold:crdkustomizeresource
|
||||||
|
|
||||||
patchesStrategicMerge:
|
patchesStrategicMerge:
|
||||||
|
|||||||
@@ -43,6 +43,18 @@ rules:
|
|||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
- watch
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
@@ -56,6 +68,46 @@ rules:
|
|||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
- watch
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- dns.openstack.org
|
||||||
|
resources:
|
||||||
|
- designates
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- dns.openstack.org
|
||||||
|
resources:
|
||||||
|
- designates/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- dns.openstack.org
|
||||||
|
resources:
|
||||||
|
- zones
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- dns.openstack.org
|
||||||
|
resources:
|
||||||
|
- zones/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- infrastructure.vexxhost.cloud
|
- infrastructure.vexxhost.cloud
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
9
config/samples/dns_v1_designate.yaml
Normal file
9
config/samples/dns_v1_designate.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: dns.openstack.org/v1
|
||||||
|
kind: Designate
|
||||||
|
metadata:
|
||||||
|
name: sample
|
||||||
|
annotations:
|
||||||
|
"dns.openstack.org/is-default-designate": "true"
|
||||||
|
spec:
|
||||||
|
credentials: rc-secret-sample
|
||||||
|
cloudname: devstack
|
||||||
10
config/samples/dns_v1_zone.yaml
Normal file
10
config/samples/dns_v1_zone.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: dns.openstack.org/v1
|
||||||
|
kind: Zone
|
||||||
|
metadata:
|
||||||
|
name: sample
|
||||||
|
annotations:
|
||||||
|
"dns.openstack.org/designate": "sample"
|
||||||
|
spec:
|
||||||
|
domain: example2.com.
|
||||||
|
ttl: 3600
|
||||||
|
email: okozachenko@gmail.com
|
||||||
178
controllers/designate_controller.go
Normal file
178
controllers/designate_controller.go
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
128
controllers/zone_controller.go
Normal file
128
controllers/zone_controller.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
corev1 "k8s.io/api/core/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/utils/baseutils"
|
||||||
|
"opendev.org/vexxhost/openstack-operator/utils/openstackutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ZoneReconciler reconciles a Zone object
|
||||||
|
type ZoneReconciler struct {
|
||||||
|
client.Client
|
||||||
|
Log logr.Logger
|
||||||
|
Scheme *runtime.Scheme
|
||||||
|
DesignateClient *openstackutils.DesignateClientBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
// +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
|
||||||
|
// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
|
||||||
|
// Reconcile does the reconcilication for create/update/delete Zone instances
|
||||||
|
func (r *ZoneReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
Zone dnsv1.Zone
|
||||||
|
designateName string
|
||||||
|
credentials corev1.Secret
|
||||||
|
designate dnsv1.Designate
|
||||||
|
)
|
||||||
|
ctx := context.Background()
|
||||||
|
log := r.Log.WithValues("zone", req.NamespacedName)
|
||||||
|
|
||||||
|
// Get Zone
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &Zone); err != nil {
|
||||||
|
log.Error(err, "unable to fetch Zone"+req.Name+":"+req.Namespace)
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the corresponding designate Name
|
||||||
|
if val, ok := Zone.Annotations[_designatingAnnotation]; ok {
|
||||||
|
designateName = val
|
||||||
|
} else if val, ok := Zone.Annotations[_defaultDesignatingAnnotation]; ok {
|
||||||
|
designateName = val
|
||||||
|
} else {
|
||||||
|
err := errors.New("no designate annotation")
|
||||||
|
log.Error(err, "No designate annotation."+req.Name+":"+req.Namespace)
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get designate instance
|
||||||
|
if err := r.Get(ctx, types.NamespacedName{
|
||||||
|
Namespace: req.Namespace,
|
||||||
|
Name: designateName,
|
||||||
|
}, &designate); err != nil {
|
||||||
|
log.Error(err, "unable to fetch corresponding designate "+req.Name+":"+req.Namespace)
|
||||||
|
return ctrl.Result{}, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Finalizer for the async deletion
|
||||||
|
zoneFinalizeName := "zone.finalizers.dns.openstack.org"
|
||||||
|
if Zone.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||||
|
if !(baseutils.ContainsString(Zone.ObjectMeta.Finalizers, zoneFinalizeName)) {
|
||||||
|
Zone.ObjectMeta.Finalizers = append(Zone.ObjectMeta.Finalizers, zoneFinalizeName)
|
||||||
|
if err := r.Update(ctx, &Zone); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if baseutils.ContainsString(Zone.ObjectMeta.Finalizers, zoneFinalizeName) {
|
||||||
|
if err := r.DesignateClient.DeleteZone(Zone.Spec.Domain); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Zone deletion using finalizer")
|
||||||
|
Zone.ObjectMeta.Finalizers = baseutils.RemoveString(Zone.ObjectMeta.Finalizers, zoneFinalizeName)
|
||||||
|
if err := r.Update(ctx, &Zone); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create or update
|
||||||
|
if err := r.DesignateClient.CreateOrUpdateZone(Zone.Spec.Domain, Zone.Spec.TTL, Zone.Spec.Email); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager initializes the controller with primary manager
|
||||||
|
func (r *ZoneReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&dnsv1.Zone{}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
6
go.mod
6
go.mod
@@ -4,9 +4,15 @@ go 1.13
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
|
||||||
|
github.com/ghodss/yaml v1.0.0
|
||||||
github.com/go-logr/logr v0.1.0
|
github.com/go-logr/logr v0.1.0
|
||||||
github.com/google/go-cmp v0.3.0
|
github.com/google/go-cmp v0.3.0
|
||||||
|
github.com/gophercloud/gophercloud v0.1.0
|
||||||
|
github.com/onsi/ginkgo v1.11.0
|
||||||
|
github.com/onsi/gomega v1.8.1
|
||||||
|
github.com/sirupsen/logrus v1.4.2
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
|
gopkg.in/yaml.v2 v2.2.4
|
||||||
k8s.io/api v0.17.2
|
k8s.io/api v0.17.2
|
||||||
k8s.io/apimachinery v0.17.2
|
k8s.io/apimachinery v0.17.2
|
||||||
k8s.io/client-go v0.17.2
|
k8s.io/client-go v0.17.2
|
||||||
|
|||||||
3
go.sum
3
go.sum
@@ -66,6 +66,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
|||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
@@ -151,6 +152,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
|
|||||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||||
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
|
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
|
||||||
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
||||||
|
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
|
||||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
@@ -248,6 +250,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
|
|||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
|||||||
75
main.go
75
main.go
@@ -1,6 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
@@ -26,9 +24,11 @@ import (
|
|||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||||
|
|
||||||
|
dnsv1 "opendev.org/vexxhost/openstack-operator/api/dns/v1"
|
||||||
monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1"
|
monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1"
|
||||||
infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1"
|
infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1"
|
||||||
"opendev.org/vexxhost/openstack-operator/controllers"
|
"opendev.org/vexxhost/openstack-operator/controllers"
|
||||||
|
"opendev.org/vexxhost/openstack-operator/utils/openstackutils"
|
||||||
"opendev.org/vexxhost/openstack-operator/version"
|
"opendev.org/vexxhost/openstack-operator/version"
|
||||||
// +kubebuilder:scaffold:imports
|
// +kubebuilder:scaffold:imports
|
||||||
)
|
)
|
||||||
@@ -40,9 +40,9 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
_ = clientgoscheme.AddToScheme(scheme)
|
_ = clientgoscheme.AddToScheme(scheme)
|
||||||
|
|
||||||
_ = infrastructurev1alpha1.AddToScheme(scheme)
|
_ = infrastructurev1alpha1.AddToScheme(scheme)
|
||||||
_ = monitoringv1.AddToScheme(scheme)
|
_ = monitoringv1.AddToScheme(scheme)
|
||||||
|
_ = dnsv1.AddToScheme(scheme)
|
||||||
// +kubebuilder:scaffold:scheme
|
// +kubebuilder:scaffold:scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,6 @@ func main() {
|
|||||||
"Enable leader election for controller manager. "+
|
"Enable leader election for controller manager. "+
|
||||||
"Enabling this will ensure there is only one active controller manager.")
|
"Enabling this will ensure there is only one active controller manager.")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
|
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
|
||||||
|
|
||||||
// Create manager
|
// Create manager
|
||||||
@@ -70,8 +69,53 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get Designate client
|
||||||
|
designateClientBuilder := new(openstackutils.DesignateClientBuilder)
|
||||||
|
designateClientBuilder.SetAuthFailed()
|
||||||
|
|
||||||
// Setup controllers with manager
|
// Setup controllers with manager
|
||||||
if err = (&controllers.McrouterReconciler{
|
setupMcrouterReconciler(mgr)
|
||||||
|
setupMemcachedReconciler(mgr)
|
||||||
|
setupZoneReconciler(mgr, designateClientBuilder)
|
||||||
|
setupDesignateReconciler(mgr, designateClientBuilder)
|
||||||
|
|
||||||
|
// +kubebuilder:scaffold:builder
|
||||||
|
setupLog.Info("starting manager", "revision", version.Revision)
|
||||||
|
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||||
|
setupLog.Error(err, "problem running manager")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupZoneReconciler setups the Zone controller with manager
|
||||||
|
func setupZoneReconciler(mgr ctrl.Manager, designateClientBuilder *openstackutils.DesignateClientBuilder) {
|
||||||
|
if err := (&controllers.ZoneReconciler{
|
||||||
|
Client: mgr.GetClient(),
|
||||||
|
Log: ctrl.Log.WithName("controllers").WithName("Zone"),
|
||||||
|
Scheme: mgr.GetScheme(),
|
||||||
|
DesignateClient: designateClientBuilder,
|
||||||
|
}).SetupWithManager(mgr); err != nil {
|
||||||
|
setupLog.Error(err, "unable to create controller", "controller", "Zone")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupDesignateReconciler setups the Designate controller with manager
|
||||||
|
func setupDesignateReconciler(mgr ctrl.Manager, designateClientBuilder *openstackutils.DesignateClientBuilder) {
|
||||||
|
if err := (&controllers.DesignateReconciler{
|
||||||
|
Client: mgr.GetClient(),
|
||||||
|
Log: ctrl.Log.WithName("controllers").WithName("Zone"),
|
||||||
|
Scheme: mgr.GetScheme(),
|
||||||
|
DesignateClient: designateClientBuilder,
|
||||||
|
}).SetupWithManager(mgr); err != nil {
|
||||||
|
setupLog.Error(err, "unable to create controller", "controller", "Designate")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupMcrouterReconciler setups the Mcrouter controller with manager
|
||||||
|
func setupMcrouterReconciler(mgr ctrl.Manager) {
|
||||||
|
if err := (&controllers.McrouterReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Log: ctrl.Log.WithName("controllers").WithName("Mcrouter"),
|
Log: ctrl.Log.WithName("controllers").WithName("Mcrouter"),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
@@ -79,8 +123,11 @@ func main() {
|
|||||||
setupLog.Error(err, "unable to create controller", "controller", "Mcrouter")
|
setupLog.Error(err, "unable to create controller", "controller", "Mcrouter")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err = (&controllers.MemcachedReconciler{
|
// setupMemcachedReconciler setups the Memcached controller with manager
|
||||||
|
func setupMemcachedReconciler(mgr ctrl.Manager) {
|
||||||
|
if err := (&controllers.MemcachedReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Log: ctrl.Log.WithName("controllers").WithName("Memcached"),
|
Log: ctrl.Log.WithName("controllers").WithName("Memcached"),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
@@ -88,20 +135,4 @@ func main() {
|
|||||||
setupLog.Error(err, "unable to create controller", "controller", "Memcached")
|
setupLog.Error(err, "unable to create controller", "controller", "Memcached")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = (&controllers.RabbitmqReconciler{
|
|
||||||
Client: mgr.GetClient(),
|
|
||||||
Log: ctrl.Log.WithName("controllers").WithName("Rabbitmq"),
|
|
||||||
Scheme: mgr.GetScheme(),
|
|
||||||
}).SetupWithManager(mgr); err != nil {
|
|
||||||
setupLog.Error(err, "unable to create controller", "controller", "Rabbitmq")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
// +kubebuilder:scaffold:builder
|
|
||||||
|
|
||||||
setupLog.Info("starting manager", "revision", version.Revision)
|
|
||||||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
|
||||||
setupLog.Error(err, "problem running manager")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
15
utils/baseutils/codec.go
Normal file
15
utils/baseutils/codec.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package baseutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Base64DecodeByte2Str returns plain text as string from the encrypted text as byte array
|
||||||
|
func Base64DecodeByte2Str(enc []byte) string {
|
||||||
|
encStr := string(enc)
|
||||||
|
decStr, err := base64.StdEncoding.DecodeString(encStr)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(decStr)
|
||||||
|
}
|
||||||
54
utils/baseutils/slice.go
Normal file
54
utils/baseutils/slice.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package baseutils
|
||||||
|
|
||||||
|
// CompareStrSlice compares two string slices and return the different elements
|
||||||
|
// Return values are 2 arrays; aOnlySlice, and bOnlySlice
|
||||||
|
func CompareStrSlice(aS []string, bS []string) ([]string, []string) {
|
||||||
|
aOnlyS := []string{}
|
||||||
|
for _, a := range aS {
|
||||||
|
i, isExist := Find(bS, a)
|
||||||
|
if !isExist {
|
||||||
|
aOnlyS = append(aOnlyS, a)
|
||||||
|
} else {
|
||||||
|
RemoveElement(&bS, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aOnlyS, bS
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find is a helper function to find the string in a slice of strings.
|
||||||
|
func Find(slice []string, val string) (int, bool) {
|
||||||
|
for i, item := range slice {
|
||||||
|
if item == val {
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveElement is a helper function to remove the ith string from a slice of strings.
|
||||||
|
func RemoveElement(a *[]string, i int) {
|
||||||
|
(*a)[i] = (*a)[len(*a)-1] // Copy last element to index i.
|
||||||
|
(*a)[len(*a)-1] = "" // Erase last element (write zero value).
|
||||||
|
(*a) = (*a)[:len(*a)-1] // Truncate the length
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsString is a helper function to check string in a slice of strings
|
||||||
|
func ContainsString(slice []string, s string) bool {
|
||||||
|
for _, item := range slice {
|
||||||
|
if item == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveString is a helper function to remove string from a slice of strings.
|
||||||
|
func RemoveString(slice []string, s string) (result []string) {
|
||||||
|
for _, item := range slice {
|
||||||
|
if item == s {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
297
utils/openstackutils/designate.go
Normal file
297
utils/openstackutils/designate.go
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
package openstackutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"opendev.org/vexxhost/openstack-operator/utils/tlsutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_openAPIVersion = "3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DesignateClientBuilder is an implementation of the designateClientInterface
|
||||||
|
type DesignateClientBuilder struct {
|
||||||
|
ServiceClient *gophercloud.ServiceClient
|
||||||
|
isAuth bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloudYAML is for parsing the clouds.yaml
|
||||||
|
type CloudYAML struct {
|
||||||
|
Clouds map[string]struct {
|
||||||
|
Auth struct {
|
||||||
|
Auth_url string `yaml:"auth_url"`
|
||||||
|
Project_name string `yaml:"project_name"`
|
||||||
|
Project_id string `yaml:"project_id"`
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
User_domain_name string `yaml:"user_domain_name"`
|
||||||
|
Project_domain_name string `yaml:"project_domain_name"`
|
||||||
|
} `yaml:"auth"`
|
||||||
|
Region_name string `yaml:"region_name"`
|
||||||
|
Interface string `yaml:"interface"`
|
||||||
|
} `yaml:"clouds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DesignateClient is a constructor for the DesignateBuilder
|
||||||
|
func DesignateClient(existing *DesignateClientBuilder, rc []byte, cloudName string) error {
|
||||||
|
if existing.GetAuthStatus() {
|
||||||
|
log.Infof("Already authenticated")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := setAuthSettings(rc, cloudName); err != nil {
|
||||||
|
log.Infof("Authentication failed - %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serviceClient, err := createDesignateServiceClient()
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("createDesignateServiceClient failed - %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
existing.ServiceClient = serviceClient
|
||||||
|
existing.SetAuthSuccess()
|
||||||
|
log.Infof("Authentication success!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateZone creates a zone
|
||||||
|
func (c *DesignateClientBuilder) CreateZone(dn string, ttl int, email string) (string, error) {
|
||||||
|
// zone create
|
||||||
|
|
||||||
|
createOpts := zones.CreateOpts{
|
||||||
|
Name: dn,
|
||||||
|
Email: email,
|
||||||
|
Type: "PRIMARY",
|
||||||
|
TTL: ttl,
|
||||||
|
Description: "This is a zone.",
|
||||||
|
}
|
||||||
|
|
||||||
|
res := zones.Create(c.ServiceClient, createOpts)
|
||||||
|
if res.Err != nil {
|
||||||
|
log.Errorf("Create Zone failed - %s", res.Err.Error())
|
||||||
|
c.SetAuthFailed()
|
||||||
|
return "", res.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Gained zone infor successfully")
|
||||||
|
zoneInfo, err := res.Extract()
|
||||||
|
if err != nil {
|
||||||
|
c.SetAuthFailed()
|
||||||
|
log.Errorf("Extract zone infor failed")
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return zoneInfo.ID, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateZone updates zone TTL and Email.
|
||||||
|
func (c *DesignateClientBuilder) UpdateZone(zoneID string, TTL int, Email string) error {
|
||||||
|
updateOpts := zones.UpdateOpts{
|
||||||
|
TTL: TTL,
|
||||||
|
Email: Email,
|
||||||
|
}
|
||||||
|
if err := zones.Update(c.ServiceClient, zoneID, updateOpts).Err; err != nil {
|
||||||
|
log.Errorf("Update zone failed")
|
||||||
|
c.SetAuthFailed()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteZone deletes a zone
|
||||||
|
func (c *DesignateClientBuilder) DeleteZone(Domain string) error {
|
||||||
|
zoneList, err := c.ListZone()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, zone := range zoneList {
|
||||||
|
if zone.Name == Domain {
|
||||||
|
return c.deleteZoneByID(zone.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Infof("No such zone exists to delete.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListZone gets the zone list
|
||||||
|
func (c *DesignateClientBuilder) ListZone() ([]zones.Zone, error) {
|
||||||
|
listOpts := zones.ListOpts{}
|
||||||
|
allPages, err := zones.List(c.ServiceClient, listOpts).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("List zone list failed")
|
||||||
|
c.SetAuthFailed()
|
||||||
|
return []zones.Zone{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allZones, err := zones.ExtractZones(allPages)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Extract zone infor from the zone list failed")
|
||||||
|
c.SetAuthFailed()
|
||||||
|
return []zones.Zone{}, err
|
||||||
|
}
|
||||||
|
return allZones, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrUpdateZone sync the zone list
|
||||||
|
func (c *DesignateClientBuilder) CreateOrUpdateZone(Domain string, TTL int, Email string) error {
|
||||||
|
zoneList, err := c.ListZone()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, zone := range zoneList {
|
||||||
|
if Domain == zone.Name {
|
||||||
|
// Update Zone
|
||||||
|
log.Infof("Designate: Zone %s already exists", zone.Name)
|
||||||
|
return c.UpdateZone(zone.ID, TTL, Email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create zone
|
||||||
|
_, err = c.CreateZone(Domain, TTL, Email)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteZoneByID deletes the zone using zoneID, consuming the designate API directly
|
||||||
|
func (c *DesignateClientBuilder) deleteZoneByID(zoneID string) error {
|
||||||
|
if err := zones.Delete(c.ServiceClient, zoneID).Err; err != nil {
|
||||||
|
c.SetAuthFailed()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAuthSuccess means the current client already authenticated
|
||||||
|
func (c *DesignateClientBuilder) SetAuthSuccess() {
|
||||||
|
c.isAuth = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAuthFailed means the current client needs to authenticate
|
||||||
|
func (c *DesignateClientBuilder) SetAuthFailed() {
|
||||||
|
c.isAuth = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthStatus returns the authentication status
|
||||||
|
func (c *DesignateClientBuilder) GetAuthStatus() bool {
|
||||||
|
return c.isAuth
|
||||||
|
}
|
||||||
|
|
||||||
|
// authenticate in OpenStack and obtain Designate service endpoint
|
||||||
|
func createDesignateServiceClient() (*gophercloud.ServiceClient, error) {
|
||||||
|
opts, err := getAuthSettings()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Infof("Using OpenStack Keystone at %s", opts.IdentityEndpoint)
|
||||||
|
authProvider, err := openstack.NewClient(opts.IdentityEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig, err := tlsutils.CreateTLSConfig("OPENSTACK")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
}).DialContext,
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
TLSClientConfig: tlsConfig,
|
||||||
|
}
|
||||||
|
authProvider.HTTPClient.Transport = transport
|
||||||
|
|
||||||
|
if err = openstack.Authenticate(authProvider, opts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
eo := gophercloud.EndpointOpts{
|
||||||
|
Region: os.Getenv("OS_REGION_NAME"),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := openstack.NewDNSV2(authProvider, eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Infof("Found OpenStack Designate service at %s", client.Endpoint)
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns OpenStack Keystone authentication settings by obtaining values from standard environment variables.
|
||||||
|
// also fixes incompatibilities between gophercloud implementation and *-stackrc files that can be downloaded
|
||||||
|
// from OpenStack dashboard in latest versions
|
||||||
|
func getAuthSettings() (gophercloud.AuthOptions, error) {
|
||||||
|
remapEnv(map[string]string{
|
||||||
|
"OS_TENANT_NAME": "OS_PROJECT_NAME",
|
||||||
|
"OS_TENANT_ID": "OS_PROJECT_ID",
|
||||||
|
"OS_DOMAIN_NAME": "OS_USER_DOMAIN_NAME",
|
||||||
|
"OS_DOMAIN_ID": "OS_USER_DOMAIN_ID",
|
||||||
|
})
|
||||||
|
|
||||||
|
opts, err := openstack.AuthOptionsFromEnv()
|
||||||
|
if err != nil {
|
||||||
|
return gophercloud.AuthOptions{}, err
|
||||||
|
}
|
||||||
|
opts.AllowReauth = true
|
||||||
|
if !strings.HasSuffix(opts.IdentityEndpoint, "/") {
|
||||||
|
opts.IdentityEndpoint += "/"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(opts.IdentityEndpoint, "/v2.0/") && !strings.HasSuffix(opts.IdentityEndpoint, "/v3/") {
|
||||||
|
opts.IdentityEndpoint += "v2.0/"
|
||||||
|
}
|
||||||
|
return opts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copies environment variables to new names without overwriting existing values
|
||||||
|
func remapEnv(mapping map[string]string) {
|
||||||
|
for k, v := range mapping {
|
||||||
|
currentVal := os.Getenv(k)
|
||||||
|
newVal := os.Getenv(v)
|
||||||
|
if currentVal == "" && newVal != "" {
|
||||||
|
os.Setenv(k, newVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setAuthSettings(rc []byte, cloudName string) error {
|
||||||
|
var cloudYaml CloudYAML
|
||||||
|
parseCloudYAML(rc, &cloudYaml)
|
||||||
|
credential, ok := cloudYaml.Clouds[cloudName]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("rc secret does not involve the current cloud credential ")
|
||||||
|
}
|
||||||
|
os.Setenv("OS_AUTH_URL", credential.Auth.Auth_url)
|
||||||
|
os.Setenv("OS_PROJECT_ID", credential.Auth.Project_id)
|
||||||
|
os.Setenv("OS_PROJECT_NAME", credential.Auth.Project_name)
|
||||||
|
os.Setenv("OS_USER_DOMAIN_NAME", credential.Auth.User_domain_name)
|
||||||
|
os.Setenv("OS_USERNAME", credential.Auth.Username)
|
||||||
|
os.Setenv("OS_PASSWORD", credential.Auth.Password)
|
||||||
|
os.Setenv("OS_REGION_NAME", credential.Region_name)
|
||||||
|
os.Setenv("OS_INTERFACE", credential.Interface)
|
||||||
|
os.Setenv("OS_IDENTITY_API_VERSION", _openAPIVersion)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCloudYAML(y []byte, cloudYaml *CloudYAML) {
|
||||||
|
err := yaml.Unmarshal([]byte(y), cloudYaml)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
73
utils/tlsutils/tls.go
Normal file
73
utils/tlsutils/tls.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package tlsutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultMinVersion = 0
|
||||||
|
|
||||||
|
// CreateTLSConfig creates tls.Config instance from TLS parameters passed in environment variables with the given prefix
|
||||||
|
func CreateTLSConfig(prefix string) (*tls.Config, error) {
|
||||||
|
caFile := os.Getenv(fmt.Sprintf("%s_CA_FILE", prefix))
|
||||||
|
certFile := os.Getenv(fmt.Sprintf("%s_CERT_FILE", prefix))
|
||||||
|
keyFile := os.Getenv(fmt.Sprintf("%s_KEY_FILE", prefix))
|
||||||
|
serverName := os.Getenv(fmt.Sprintf("%s_TLS_SERVER_NAME", prefix))
|
||||||
|
isInsecureStr := strings.ToLower(os.Getenv(fmt.Sprintf("%s_TLS_INSECURE", prefix)))
|
||||||
|
isInsecure := isInsecureStr == "true" || isInsecureStr == "yes" || isInsecureStr == "1"
|
||||||
|
tlsConfig, err := NewTLSConfig(certFile, keyFile, caFile, serverName, isInsecure, defaultMinVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tlsConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLSConfig creates a tls.Config instance from directly-passed parameters, loading the ca, cert, and key from disk
|
||||||
|
func NewTLSConfig(certPath, keyPath, caPath, serverName string, insecure bool, minVersion uint16) (*tls.Config, error) {
|
||||||
|
if certPath != "" && keyPath == "" || certPath == "" && keyPath != "" {
|
||||||
|
return nil, errors.New("either both cert and key or none must be provided")
|
||||||
|
}
|
||||||
|
var certificates []tls.Certificate
|
||||||
|
if certPath != "" {
|
||||||
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not load TLS cert: %s", err)
|
||||||
|
}
|
||||||
|
certificates = append(certificates, cert)
|
||||||
|
}
|
||||||
|
roots, err := loadRoots(caPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tls.Config{
|
||||||
|
MinVersion: minVersion,
|
||||||
|
Certificates: certificates,
|
||||||
|
RootCAs: roots,
|
||||||
|
InsecureSkipVerify: insecure,
|
||||||
|
ServerName: serverName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loads CA cert
|
||||||
|
func loadRoots(caPath string) (*x509.CertPool, error) {
|
||||||
|
if caPath == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
pem, err := ioutil.ReadFile(caPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading %s: %s", caPath, err)
|
||||||
|
}
|
||||||
|
ok := roots.AppendCertsFromPEM(pem)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("could not read root certs: %s", err)
|
||||||
|
}
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user