Add Zone and Designate CRs
Change-Id: I7b59fc5ae66adb4d123c10249e2321c6bc6537c5
This commit is contained in:
parent
5acd3683ad
commit
1d83657ec1
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
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
@ -56,6 +68,46 @@ rules:
|
||||
- patch
|
||||
- update
|
||||
- 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:
|
||||
- infrastructure.vexxhost.cloud
|
||||
resources:
|
||||
|
@ -6,4 +6,10 @@
|
||||
{{- range $path, $bytes := .Files.Glob "crds/monitoring.coreos.com*.yaml" }}
|
||||
{{ $.Files.Get $path }}
|
||||
{{- 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:
|
||||
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/monitoring.coreos.com_podmonitors.yaml
|
||||
- bases/monitoring.coreos.com_prometheusrules.yaml
|
||||
- bases/dns.openstack.org_zones.yaml
|
||||
- bases/dns.openstack.org_designates.yaml
|
||||
# +kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
patchesStrategicMerge:
|
||||
|
@ -43,6 +43,18 @@ rules:
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
@ -56,6 +68,46 @@ rules:
|
||||
- patch
|
||||
- update
|
||||
- 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:
|
||||
- infrastructure.vexxhost.cloud
|
||||
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 (
|
||||
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/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
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
k8s.io/api v0.17.2
|
||||
k8s.io/apimachinery 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/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
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/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=
|
||||
@ -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.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
|
||||
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/gorilla/websocket v0.0.0-20170926233335-4201258b820c/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/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.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
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/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");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
@ -26,9 +24,11 @@ import (
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"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"
|
||||
infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1"
|
||||
"opendev.org/vexxhost/openstack-operator/controllers"
|
||||
"opendev.org/vexxhost/openstack-operator/utils/openstackutils"
|
||||
"opendev.org/vexxhost/openstack-operator/version"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
@ -40,9 +40,9 @@ var (
|
||||
|
||||
func init() {
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
|
||||
_ = infrastructurev1alpha1.AddToScheme(scheme)
|
||||
_ = monitoringv1.AddToScheme(scheme)
|
||||
_ = dnsv1.AddToScheme(scheme)
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
@ -54,7 +54,6 @@ func main() {
|
||||
"Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
flag.Parse()
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
|
||||
|
||||
// Create manager
|
||||
@ -70,8 +69,53 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get Designate client
|
||||
designateClientBuilder := new(openstackutils.DesignateClientBuilder)
|
||||
designateClientBuilder.SetAuthFailed()
|
||||
|
||||
// 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(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("Mcrouter"),
|
||||
Scheme: mgr.GetScheme(),
|
||||
@ -79,8 +123,11 @@ func main() {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Mcrouter")
|
||||
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(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("Memcached"),
|
||||
Scheme: mgr.GetScheme(),
|
||||
@ -88,20 +135,4 @@ func main() {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Memcached")
|
||||
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
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user