Skip to content

Commit

Permalink
Update Workload Deployment and Service propagation adding workload re…
Browse files Browse the repository at this point in the history
…ference label and testing for the target deployment/service collisions.

Signed-off-by: Illya Chekrygin <illya.chekrygin@gmail.com>
  • Loading branch information
ichekrygin committed Feb 2, 2019
1 parent 66c98d6 commit 6791236
Show file tree
Hide file tree
Showing 7 changed files with 481 additions and 50 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
/config
cover.out
/vendor
/.vendor-new
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,17 @@ spec:
- targetNamespace
- targetDeployment
- targetService
- resources
type: object
status:
properties:
deployment:
type: object
deploymentRef:
type: object
service:
type: object
serviceRef:
type: object
state:
type: string
type: object
Expand Down
6 changes: 4 additions & 2 deletions pkg/apis/compute/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,17 @@ type WorkloadSpec struct {
TargetService *corev1.Service `json:"targetService"`

// Resources
Resources []ResourceReference `json:"resources"`
Resources []ResourceReference `json:"resources,omitempty"`
}

// WorkloadStatus
type WorkloadStatus struct {
corev1alpha1.ConditionedStatus
appsv1.DeploymentStatus `json:"deployment,omitempty"`
corev1.ServiceStatus `json:"service,omitempty"`
State WorkloadState `json:"state,omitempty"`
State WorkloadState `json:"state,omitempty"`
Deployment *corev1.ObjectReference `json:"deploymentRef,omitempty"`
Service *corev1.ObjectReference `json:"serviceRef,omitempty"`
}

// +genclient
Expand Down
10 changes: 10 additions & 0 deletions pkg/apis/compute/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 106 additions & 17 deletions pkg/controller/compute/workload/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const (
errorCreating = "Failed to create"
errorSynchronizing = "Failed to sync"
errorDeleting = "Failed to delete"

workloadReferenceLabelKey = "workloadRef"
)

var (
Expand All @@ -74,20 +76,26 @@ type Reconciler struct {
create func(*computev1alpha1.Workload, kubernetes.Interface) (reconcile.Result, error)
sync func(*computev1alpha1.Workload, kubernetes.Interface) (reconcile.Result, error)
delete func(*computev1alpha1.Workload, kubernetes.Interface) (reconcile.Result, error)

propagateDeployment func(kubernetes.Interface, *appsv1.Deployment, string, string) (*appsv1.Deployment, error)
propagateService func(kubernetes.Interface, *corev1.Service, string, string) (*corev1.Service, error)
}

// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
r := &Reconciler{
Client: mgr.GetClient(),
scheme: mgr.GetScheme(),
kubeclient: kubernetes.NewForConfigOrDie(mgr.GetConfig()),
recorder: mgr.GetRecorder(controllerName),
Client: mgr.GetClient(),
scheme: mgr.GetScheme(),
kubeclient: kubernetes.NewForConfigOrDie(mgr.GetConfig()),
recorder: mgr.GetRecorder(controllerName),
propagateDeployment: propagateDeployment,
propagateService: propagateService,
}
r.connect = r._connect
r.create = r._create
r.sync = r._sync
r.delete = r._delete

return r
}

Expand Down Expand Up @@ -166,6 +174,85 @@ func (r *Reconciler) _connect(instance *computev1alpha1.Workload) (kubernetes.In
return kubernetes.NewForConfig(config)
}

func addWorkloadReferenceLabel(m *metav1.ObjectMeta, uid string) {
if m.Labels == nil {
m.Labels = make(map[string]string)
}
m.Labels[workloadReferenceLabelKey] = uid
}

func getWorkloadReferenceLabel(m metav1.ObjectMeta) string {
if m.Labels == nil {
return ""
}
return m.Labels[workloadReferenceLabelKey]
}

// propagateDeployment to the target cluster
func propagateDeployment(k kubernetes.Interface, d *appsv1.Deployment, ns, uid string) (*appsv1.Deployment, error) {
// Update deployment selector - typically selector value is not provided and if it is not
// matching template the deployment create operation will fail
if d.Spec.Selector == nil {
d.Spec.Selector = &metav1.LabelSelector{}
}
d.Spec.Selector.MatchLabels = d.Spec.Template.Labels

// If deployment namespace value is not provided - default it to the workload target namespace
d.Namespace = util.IfEmptyString(d.Namespace, ns)

addWorkloadReferenceLabel(&d.ObjectMeta, uid)

// Check if target deployment already exists on the target cluster
dd, err := k.AppsV1().Deployments(d.Namespace).Get(d.Name, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
dd = nil
} else {
return nil, err
}
}

if dd == nil {
return k.AppsV1().Deployments(d.Namespace).Create(d)
}

l := getWorkloadReferenceLabel(dd.ObjectMeta)
if l == string(uid) {
return k.AppsV1().Deployments(d.Namespace).Update(d)
}

return nil, fmt.Errorf("cannot propagate, deployment %s/%s already exists", d.Namespace, d.Name)
}

// propagateService to the target cluster
func propagateService(k kubernetes.Interface, s *corev1.Service, ns, uid string) (*corev1.Service, error) {
// If service namespace vlaue is not provided - default it to the workload target namespace
s.Namespace = util.IfEmptyString(s.Namespace, ns)

addWorkloadReferenceLabel(&s.ObjectMeta, uid)

// check if service already exists
ss, err := k.CoreV1().Services(s.Namespace).Get(s.Name, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
ss = nil
} else {
return nil, err
}
}

if ss == nil {
return k.CoreV1().Services(s.Namespace).Create(s)
}

l := getWorkloadReferenceLabel(ss.ObjectMeta)
if l == string(uid) {
return k.CoreV1().Services(s.Namespace).Update(s)
}

return nil, fmt.Errorf("cannot propagate, service %s/%s already exists", s.Namespace, s.Name)
}

// _create workload
func (r *Reconciler) _create(instance *computev1alpha1.Workload, client kubernetes.Interface) (reconcile.Result, error) {
instance.Status.SetCreating()
Expand All @@ -179,6 +266,8 @@ func (r *Reconciler) _create(instance *computev1alpha1.Workload, client kubernet
return r.fail(instance, errorCreating, err.Error())
}

uid := string(instance.UID)

// propagate resources secrets
for _, resource := range instance.Spec.Resources {
// retrieve secret
Expand All @@ -193,28 +282,26 @@ func (r *Reconciler) _create(instance *computev1alpha1.Workload, client kubernet
Name: sec.Name,
Namespace: targetNamespace,
}
addWorkloadReferenceLabel(&sec.ObjectMeta, uid)
_, err = util.ApplySecret(client, sec)
if err != nil {
return r.fail(instance, errorCreating, err.Error())
}
}

// propagate deployment
d := instance.Spec.TargetDeployment
d.Spec.Selector.MatchLabels = d.Spec.Template.Labels
d.Namespace = util.IfEmptyString(d.Namespace, targetNamespace)
_, err = util.ApplyDeployment(client, d)
d, err := r.propagateDeployment(client, instance.Spec.TargetDeployment, targetNamespace, uid)
if err != nil {
return r.fail(instance, errorCreating, err.Error())
}
instance.Status.Deployment = util.ObjectReference(d.ObjectMeta, d.APIVersion, d.Kind)

// propagate service
s := instance.Spec.TargetService
s.Namespace = util.IfEmptyString(s.Namespace, targetNamespace)
_, err = util.ApplyService(client, s)
s, err := r.propagateService(client, instance.Spec.TargetService, targetNamespace, uid)
if err != nil {
return r.fail(instance, errorCreating, err.Error())
}
instance.Status.Service = util.ObjectReference(s.ObjectMeta, s.APIVersion, s.Kind)

instance.Status.State = computev1alpha1.WorkloadStateCreating

Expand Down Expand Up @@ -255,15 +342,17 @@ func (r *Reconciler) _delete(instance *computev1alpha1.Workload, client kubernet
ns := instance.Spec.TargetNamespace

// delete service
err := client.CoreV1().Services(ns).Delete(instance.Spec.TargetService.Name, &metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return r.fail(instance, errorDeleting, err.Error())
if s := instance.Status.Service; s != nil {
if err := client.CoreV1().Services(s.Namespace).Delete(s.Name, &metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) {
return r.fail(instance, errorDeleting, err.Error())
}
}

// delete deployment
err = client.AppsV1().Deployments(ns).Delete(instance.Spec.TargetDeployment.Name, &metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return r.fail(instance, errorDeleting, err.Error())
if d := instance.Status.Deployment; d != nil {
if err := client.AppsV1().Deployments(d.Namespace).Delete(d.Name, &metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) {
return r.fail(instance, errorDeleting, err.Error())
}
}

// delete resources secrets
Expand Down
Loading

0 comments on commit 6791236

Please sign in to comment.