diff --git a/controllers/gce/Makefile b/controllers/gce/Makefile index 3763af797..fa4db4c81 100644 --- a/controllers/gce/Makefile +++ b/controllers/gce/Makefile @@ -1,7 +1,7 @@ all: push # 0.0 shouldn't clobber any released builds -TAG = 0.6.2 +TAG = 0.6.3 PREFIX = gcr.io/google_containers/glbc server: diff --git a/controllers/gce/main.go b/controllers/gce/main.go index dcd9413d0..6095990e5 100644 --- a/controllers/gce/main.go +++ b/controllers/gce/main.go @@ -32,6 +32,7 @@ import ( "k8s.io/kubernetes/pkg/api" client "k8s.io/kubernetes/pkg/client/unversioned" kubectl_util "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/util/wait" "github.com/golang/glog" @@ -191,8 +192,11 @@ func main() { if *inCluster || *useRealCloud { // Create cluster manager - clusterManager, err = controller.NewClusterManager( - *clusterName, defaultBackendNodePort, *healthCheckPath, storage.NewConfigMapVault(kubeClient, api.NamespaceSystem, uidConfigMapName)) + name, err := getClusterUID(kubeClient, *clusterName) + if err != nil { + glog.Fatalf("%v", err) + } + clusterManager, err = controller.NewClusterManager(name, defaultBackendNodePort, *healthCheckPath) if err != nil { glog.Fatalf("%v", err) } @@ -219,6 +223,60 @@ func main() { } } +// getClusterUID returns the cluster UID. Rules for UID generation: +// If the user specifies a --cluster-uid param it overwrites everything +// else, check UID config map for a previously recorded uid +// else, check if there are any working Ingresses +// - remember that "" is the cluster uid +// else, allocate a new uid +func getClusterUID(kubeClient *client.Client, name string) (string, error) { + cfgVault := storage.NewConfigMapVault(kubeClient, api.NamespaceSystem, uidConfigMapName) + if name != "" { + glog.Infof("Using user provided cluster uid %v", name) + // Don't save the uid in the vault, so users can rollback through + // --cluster-uid="" + return name, nil + } + + existingUID, found, err := cfgVault.Get() + if found { + glog.Infof("Using saved cluster uid %q", name) + return existingUID, nil + } else if err != nil { + // This can fail because of: + // 1. No such config map - found=false, err=nil + // 2. No such key in config map - found=false, err=nil + // 3. Apiserver flake - found=false, err!=nil + // It is not safe to proceed in 3. + return "", fmt.Errorf("Failed to retrieve current uid: %v, using %q as name", err, name) + } + + // Check if the cluster has an Ingress with ip + ings, err := kubeClient.Extensions().Ingress(api.NamespaceAll).List(api.ListOptions{LabelSelector: labels.Everything()}) + if err != nil { + return "", err + } + for _, ing := range ings.Items { + if len(ing.Status.LoadBalancer.Ingress) != 0 { + glog.Infof("Found a working Ingress, assuming uid is empty string") + return "", cfgVault.Put("") + } + } + + // Allocate new uid + f, err := os.Open("/dev/urandom") + if err != nil { + return "", err + } + defer f.Close() + b := make([]byte, 8) + if _, err := f.Read(b); err != nil { + return "", err + } + uid := fmt.Sprintf("%x", b) + return uid, cfgVault.Put(uid) +} + // getNodePort waits for the Service, and returns it's first node port. func getNodePort(client *client.Client, ns, name string) (nodePort int64, err error) { var svc *api.Service diff --git a/controllers/gce/storage/configmaps.go b/controllers/gce/storage/configmaps.go index deba3b737..d1140b126 100644 --- a/controllers/gce/storage/configmaps.go +++ b/controllers/gce/storage/configmaps.go @@ -54,13 +54,13 @@ func (c *ConfigMapVault) Get() (string, bool, error) { key := fmt.Sprintf("%v/%v", c.namespace, c.name) item, found, err := c.ConfigMapStore.GetByKey(key) if err != nil || !found { - return "", found, err + return "", false, err } cfg := item.(*api.ConfigMap) if k, ok := cfg.Data[uidDataKey]; ok { - return k, false, nil + return k, true, nil } - return "", found, fmt.Errorf("Found config map %v but it doesn't contain uid key: %+v", key, cfg.Data) + return "", false, fmt.Errorf("Found config map %v but it doesn't contain uid key: %+v", key, cfg.Data) } // Put stores the given UID in the cluster config map. @@ -89,7 +89,7 @@ func (c *ConfigMapVault) Put(uid string) error { } else if err := c.ConfigMapStore.Add(apiObj); err != nil { return fmt.Errorf("Failed to add %v: %v", cfgMapKey, err) } - glog.Infof("Successfully stored uid %v in config map %v", uid, cfgMapKey) + glog.Infof("Successfully stored uid %q in config map %v", uid, cfgMapKey) return nil } @@ -111,7 +111,7 @@ func NewConfigMapVault(c *client.Client, uidNs, uidConfigMapName string) *Config return &ConfigMapVault{NewConfigMapStore(c), uidNs, uidConfigMapName} } -// FakeConfigMapStore is an implementation of the ConfigMapStore that doesn't +// NewFakeConfigMapVault is an implementation of the ConfigMapStore that doesn't // persist configmaps. Only used in testing. func NewFakeConfigMapVault(ns, name string) *ConfigMapVault { return &ConfigMapVault{cache.NewStore(cache.MetaNamespaceKeyFunc), ns, name}