Skip to content

Commit

Permalink
Merge pull request #237 from rramkumar1/target-resource-lib
Browse files Browse the repository at this point in the history
Add an interface to manage target resources
  • Loading branch information
nicksardo authored Apr 20, 2018
2 parents d2036fb + fec0200 commit eadf3c4
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 0 deletions.
25 changes: 25 additions & 0 deletions pkg/target/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2018 Google Inc.
//
// 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.

package target

import (
extensions "k8s.io/api/extensions/v1beta1"
)

// TargetResourceManager is an interface to manage the k8s resources in "target" clusters.
type TargetResourceManager interface {
EnsureTargetIngress(ing *extensions.Ingress) (*extensions.Ingress, error)
DeleteTargetIngress(ing *extensions.Ingress) error
}
71 changes: 71 additions & 0 deletions pkg/target/target.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2018 Google Inc.
//
// 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.

package target

import (
"fmt"

"github.com/golang/glog"
extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/ingress-gce/pkg/annotations"
"k8s.io/ingress-gce/pkg/utils"
)

type targetResourceManager struct {
kubeClient kubernetes.Interface
}

func NewTargetResourceManager(kubeClient kubernetes.Interface) TargetResourceManager {
return &targetResourceManager{kubeClient: kubeClient}
}

var _ TargetResourceManager = &targetResourceManager{}

func (m *targetResourceManager) EnsureTargetIngress(ing *extensions.Ingress) (*extensions.Ingress, error) {
existingIng, getErr := m.kubeClient.ExtensionsV1beta1().Ingresses(ing.Namespace).Get(ing.Name, meta_v1.GetOptions{})
if getErr != nil {
if utils.IsNotFoundError(getErr) {
// Add MCI annotation to "target" ingress.
ing.Annotations[annotations.IngressClassKey] = annotations.GceMultiIngressClass
actualIng, createErr := m.kubeClient.ExtensionsV1beta1().Ingresses(ing.Namespace).Create(ing)
if createErr != nil {
return nil, fmt.Errorf("Error creating target ingress %v/%v: %v", ing.Namespace, ing.Name, createErr)
}
return actualIng, nil
}
}
// Ignore instance group annotation while comparing ingresses.
ignoreAnnotations := map[string]string{instanceGroupAnnotationKey: ""}
if !objectMetaAndSpecEquivalent(ing, existingIng, ignoreAnnotations) {
glog.V(3).Infof("Target ingress %v in cluster %v differs. Overwriting... \n")
updatedIng, updateErr := m.kubeClient.ExtensionsV1beta1().Ingresses(ing.Namespace).Update(ing)
if updateErr != nil {
return nil, fmt.Errorf("Error updating target ingress %v/%v: %v", ing.Namespace, ing.Name, updateErr)
}
return updatedIng, nil
}
return existingIng, nil

}

func (m *targetResourceManager) DeleteTargetIngress(ing *extensions.Ingress) error {
deleteErr := m.kubeClient.ExtensionsV1beta1().Ingresses(ing.Namespace).Delete(ing.Name, &meta_v1.DeleteOptions{})
if deleteErr != nil {
return fmt.Errorf("Error deleting ingress %v/%v: %v", ing.Namespace, ing.Name, deleteErr)
}
return nil
}
63 changes: 63 additions & 0 deletions pkg/target/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2018 Google Inc.
//
// 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.

package target

import (
"reflect"

meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/ingress-gce/pkg/utils"
)

var (
instanceGroupAnnotationKey = "ingress.gcp.kubernetes.io/instance-groups"
)

// objectMetaEquivalent checks if cluster-independent, user provided data in two given ObjectMeta are equal.
// If in the future the ObjectMeta structure is expanded then any field that is not populated
// by the api server should be included here.
// Ignores annotations with keys in ignoreAnnotationKeys.
// Note: copied from https://github.com/kubernetes/federation/blob/7951a643cebc3abdcd903eaff90d1383b43928d1/pkg/federation-controller/util/meta.go#L61
func objectMetaEquivalent(a, b meta_v1.ObjectMeta, ignoreAnnotationKeys map[string]string) bool {
if a.Name != b.Name {
return false
}
if a.Namespace != b.Namespace {
return false
}
if !reflect.DeepEqual(a.Labels, b.Labels) && (len(a.Labels) != 0 || len(b.Labels) != 0) {
return false
}
// Remove ignoreAnnotationKeys from the list of annotations.
aAnnotations := utils.PruneMap(a.Annotations, ignoreAnnotationKeys)
bAnnotations := utils.PruneMap(b.Annotations, ignoreAnnotationKeys)
if !reflect.DeepEqual(aAnnotations, bAnnotations) && (len(aAnnotations) != 0 || len(bAnnotations) != 0) {
return false
}
return true
}

// objectMetaAndSpecEquivalent checks if cluster-independent, user provided data in ObjectMeta
// and Spec in two given top level api objects are equivalent.
// Ignores annotations with keys in ignoreAnnotationKeys.
// Note: copied from https://github.com/kubernetes/federation/blob/7951a643cebc3abdcd903eaff90d1383b43928d1/pkg/federation-controller/util/meta.go#L79
func objectMetaAndSpecEquivalent(a, b runtime.Object, ignoreAnnotationKeys map[string]string) bool {
objectMetaA := reflect.ValueOf(a).Elem().FieldByName("ObjectMeta").Interface().(meta_v1.ObjectMeta)
objectMetaB := reflect.ValueOf(b).Elem().FieldByName("ObjectMeta").Interface().(meta_v1.ObjectMeta)
specA := reflect.ValueOf(a).Elem().FieldByName("Spec").Interface()
specB := reflect.ValueOf(b).Elem().FieldByName("Spec").Interface()
return objectMetaEquivalent(objectMetaA, objectMetaB, ignoreAnnotationKeys) && reflect.DeepEqual(specA, specB)
}
12 changes: 12 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,22 @@ func BackendServiceComparablePath(url string) string {
return fmt.Sprintf("global/%s", path_parts[1])
}

// StringsToKeyMap returns the map representation of a list of strings.
func StringsToKeyMap(strings []string) map[string]bool {
m := make(map[string]bool)
for _, s := range strings {
m[s] = true
}
return m
}

// PruneMap prunes the keys specified in ignore and returns the pruned map.
func PruneMap(m, ignore map[string]string) map[string]string {
result := map[string]string{}
for k, v := range m {
if _, exists := ignore[k]; !exists {
result[k] = v
}
}
return result
}

0 comments on commit eadf3c4

Please sign in to comment.