2016-03-15 00:33:12 +00:00
|
|
|
/*
|
2016-09-08 11:02:39 +00:00
|
|
|
Copyright 2015 The Kubernetes Authors.
|
2016-03-15 00:33:12 +00:00
|
|
|
|
|
|
|
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 storage
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
2017-02-15 00:48:07 +00:00
|
|
|
"sync"
|
2016-03-15 00:33:12 +00:00
|
|
|
|
|
|
|
"github.com/golang/glog"
|
2017-04-01 14:38:58 +00:00
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/client-go/kubernetes"
|
2017-04-07 05:27:50 +00:00
|
|
|
api_v1 "k8s.io/client-go/pkg/api/v1"
|
2017-04-01 14:38:58 +00:00
|
|
|
"k8s.io/client-go/tools/cache"
|
2016-03-15 00:33:12 +00:00
|
|
|
)
|
|
|
|
|
2017-02-15 00:48:07 +00:00
|
|
|
const (
|
|
|
|
// UidDataKey is the key used in config maps to store the UID.
|
|
|
|
UidDataKey = "uid"
|
|
|
|
// ProviderDataKey is the key used in config maps to store the Provider
|
|
|
|
// UID which we use to ensure unique firewalls.
|
2017-02-28 00:08:42 +00:00
|
|
|
ProviderDataKey = "provider-uid"
|
2017-02-15 00:48:07 +00:00
|
|
|
)
|
2016-03-15 00:33:12 +00:00
|
|
|
|
|
|
|
// ConfigMapVault stores cluster UIDs in config maps.
|
|
|
|
// It's a layer on top of ConfigMapStore that just implements the utils.uidVault
|
|
|
|
// interface.
|
|
|
|
type ConfigMapVault struct {
|
2017-02-15 00:48:07 +00:00
|
|
|
storeLock sync.Mutex
|
2016-03-15 00:33:12 +00:00
|
|
|
ConfigMapStore cache.Store
|
|
|
|
namespace string
|
|
|
|
name string
|
|
|
|
}
|
|
|
|
|
2017-02-15 00:48:07 +00:00
|
|
|
// Get retrieves the value associated to the provided 'key' from the cluster config map.
|
2016-03-15 00:33:12 +00:00
|
|
|
// If this method returns an error, it's guaranteed to be apiserver flake.
|
|
|
|
// If the error is a not found error it sets the boolean to false and
|
|
|
|
// returns and error of nil instead.
|
2017-02-15 00:48:07 +00:00
|
|
|
func (c *ConfigMapVault) Get(key string) (string, bool, error) {
|
|
|
|
keyStore := fmt.Sprintf("%v/%v", c.namespace, c.name)
|
|
|
|
item, found, err := c.ConfigMapStore.GetByKey(keyStore)
|
2016-03-15 00:33:12 +00:00
|
|
|
if err != nil || !found {
|
2016-05-20 01:22:31 +00:00
|
|
|
return "", false, err
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
2017-04-07 05:27:50 +00:00
|
|
|
data := item.(*api_v1.ConfigMap).Data
|
2017-02-15 00:48:07 +00:00
|
|
|
c.storeLock.Lock()
|
|
|
|
defer c.storeLock.Unlock()
|
|
|
|
if k, ok := data[key]; ok {
|
2016-05-20 01:22:31 +00:00
|
|
|
return k, true, nil
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
2017-02-15 00:48:07 +00:00
|
|
|
glog.Infof("Found config map %v but it doesn't contain key %v: %+v", keyStore, key, data)
|
|
|
|
return "", false, nil
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
|
|
|
|
2017-02-15 00:48:07 +00:00
|
|
|
// Put inserts a key/value pair in the cluster config map.
|
|
|
|
// If the key already exists, the value provided is stored.
|
|
|
|
func (c *ConfigMapVault) Put(key, val string) error {
|
|
|
|
c.storeLock.Lock()
|
|
|
|
defer c.storeLock.Unlock()
|
2017-04-07 05:27:50 +00:00
|
|
|
apiObj := &api_v1.ConfigMap{
|
2017-04-01 14:38:58 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-03-15 00:33:12 +00:00
|
|
|
Name: c.name,
|
|
|
|
Namespace: c.namespace,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
cfgMapKey := fmt.Sprintf("%v/%v", c.namespace, c.name)
|
|
|
|
|
|
|
|
item, exists, err := c.ConfigMapStore.GetByKey(cfgMapKey)
|
|
|
|
if err == nil && exists {
|
2017-04-07 05:27:50 +00:00
|
|
|
data := item.(*api_v1.ConfigMap).Data
|
2017-02-15 00:48:07 +00:00
|
|
|
existingVal, ok := data[key]
|
|
|
|
if ok && existingVal == val {
|
|
|
|
// duplicate, no need to update.
|
2016-03-15 00:33:12 +00:00
|
|
|
return nil
|
|
|
|
}
|
2017-02-15 00:48:07 +00:00
|
|
|
data[key] = val
|
|
|
|
apiObj.Data = data
|
|
|
|
if existingVal != val {
|
|
|
|
glog.Infof("Configmap %v has key %v but wrong value %v, updating to %v", cfgMapKey, key, existingVal, val)
|
|
|
|
} else {
|
|
|
|
glog.Infof("Configmap %v will be updated with %v = %v", cfgMapKey, key, val)
|
|
|
|
}
|
2016-03-15 00:33:12 +00:00
|
|
|
if err := c.ConfigMapStore.Update(apiObj); err != nil {
|
2017-01-06 08:12:25 +00:00
|
|
|
return fmt.Errorf("failed to update %v: %v", cfgMapKey, err)
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
2017-02-15 00:48:07 +00:00
|
|
|
} else {
|
|
|
|
apiObj.Data = map[string]string{key: val}
|
|
|
|
if err := c.ConfigMapStore.Add(apiObj); err != nil {
|
2017-01-06 08:12:25 +00:00
|
|
|
return fmt.Errorf("failed to add %v: %v", cfgMapKey, err)
|
2017-02-15 00:48:07 +00:00
|
|
|
}
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
2017-02-15 00:48:07 +00:00
|
|
|
glog.Infof("Successfully stored key %v = %v in config map %v", key, val, cfgMapKey)
|
2016-03-15 00:33:12 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-15 00:48:07 +00:00
|
|
|
// Delete deletes the ConfigMapStore.
|
2016-03-15 00:33:12 +00:00
|
|
|
func (c *ConfigMapVault) Delete() error {
|
|
|
|
cfgMapKey := fmt.Sprintf("%v/%v", c.namespace, c.name)
|
|
|
|
item, _, err := c.ConfigMapStore.GetByKey(cfgMapKey)
|
|
|
|
if err == nil {
|
|
|
|
return c.ConfigMapStore.Delete(item)
|
|
|
|
}
|
|
|
|
glog.Warningf("Couldn't find item %v in vault, unable to delete", cfgMapKey)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewConfigMapVault creates a config map client.
|
|
|
|
// This client is essentially meant to abstract out the details of
|
|
|
|
// configmaps and the API, and just store/retrieve a single value, the cluster uid.
|
2017-04-01 14:38:58 +00:00
|
|
|
func NewConfigMapVault(c kubernetes.Interface, uidNs, uidConfigMapName string) *ConfigMapVault {
|
2017-02-15 00:48:07 +00:00
|
|
|
return &ConfigMapVault{
|
|
|
|
ConfigMapStore: NewConfigMapStore(c),
|
|
|
|
namespace: uidNs,
|
|
|
|
name: uidConfigMapName}
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
|
|
|
|
2016-05-20 01:22:31 +00:00
|
|
|
// NewFakeConfigMapVault is an implementation of the ConfigMapStore that doesn't
|
2016-03-15 00:33:12 +00:00
|
|
|
// persist configmaps. Only used in testing.
|
|
|
|
func NewFakeConfigMapVault(ns, name string) *ConfigMapVault {
|
2017-02-15 00:48:07 +00:00
|
|
|
return &ConfigMapVault{
|
|
|
|
ConfigMapStore: cache.NewStore(cache.MetaNamespaceKeyFunc),
|
|
|
|
namespace: ns,
|
|
|
|
name: name}
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ConfigMapStore wraps the store interface. Implementations usually persist
|
|
|
|
// contents of the store transparently.
|
|
|
|
type ConfigMapStore interface {
|
|
|
|
cache.Store
|
|
|
|
}
|
|
|
|
|
2016-11-11 01:45:20 +00:00
|
|
|
// APIServerConfigMapStore only services Add and GetByKey from apiserver.
|
2016-03-15 00:33:12 +00:00
|
|
|
// TODO: Implement all the other store methods and make this a write
|
|
|
|
// through cache.
|
2016-11-11 01:45:20 +00:00
|
|
|
type APIServerConfigMapStore struct {
|
2016-03-15 00:33:12 +00:00
|
|
|
ConfigMapStore
|
2017-04-01 14:38:58 +00:00
|
|
|
client kubernetes.Interface
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add adds the given config map to the apiserver's store.
|
2016-11-11 01:45:20 +00:00
|
|
|
func (a *APIServerConfigMapStore) Add(obj interface{}) error {
|
2017-04-07 05:27:50 +00:00
|
|
|
cfg := obj.(*api_v1.ConfigMap)
|
2016-11-10 23:31:49 +00:00
|
|
|
_, err := a.client.Core().ConfigMaps(cfg.Namespace).Create(cfg)
|
2016-03-15 00:33:12 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update updates the existing config map object.
|
2016-11-11 01:45:20 +00:00
|
|
|
func (a *APIServerConfigMapStore) Update(obj interface{}) error {
|
2017-04-07 05:27:50 +00:00
|
|
|
cfg := obj.(*api_v1.ConfigMap)
|
2016-11-10 23:31:49 +00:00
|
|
|
_, err := a.client.Core().ConfigMaps(cfg.Namespace).Update(cfg)
|
2016-03-15 00:33:12 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete deletes the existing config map object.
|
2016-11-11 01:45:20 +00:00
|
|
|
func (a *APIServerConfigMapStore) Delete(obj interface{}) error {
|
2017-04-07 05:27:50 +00:00
|
|
|
cfg := obj.(*api_v1.ConfigMap)
|
2017-04-01 14:38:58 +00:00
|
|
|
return a.client.Core().ConfigMaps(cfg.Namespace).Delete(cfg.Name, &metav1.DeleteOptions{})
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetByKey returns the config map for a given key.
|
|
|
|
// The key must take the form namespace/name.
|
2016-11-11 01:45:20 +00:00
|
|
|
func (a *APIServerConfigMapStore) GetByKey(key string) (item interface{}, exists bool, err error) {
|
2016-03-15 00:33:12 +00:00
|
|
|
nsName := strings.Split(key, "/")
|
|
|
|
if len(nsName) != 2 {
|
2017-01-06 08:12:25 +00:00
|
|
|
return nil, false, fmt.Errorf("failed to get key %v, unexpecte format, expecting ns/name", key)
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|
|
|
|
ns, name := nsName[0], nsName[1]
|
2017-04-01 14:38:58 +00:00
|
|
|
cfg, err := a.client.Core().ConfigMaps(ns).Get(name, metav1.GetOptions{})
|
2016-03-15 00:33:12 +00:00
|
|
|
if err != nil {
|
|
|
|
// Translate not found errors to found=false, err=nil
|
|
|
|
if errors.IsNotFound(err) {
|
|
|
|
return nil, false, nil
|
|
|
|
}
|
|
|
|
return nil, false, err
|
|
|
|
}
|
|
|
|
return cfg, true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewConfigMapStore returns a config map store capable of persisting updates
|
|
|
|
// to apiserver.
|
2017-04-01 14:38:58 +00:00
|
|
|
func NewConfigMapStore(c kubernetes.Interface) ConfigMapStore {
|
2016-11-11 01:45:20 +00:00
|
|
|
return &APIServerConfigMapStore{ConfigMapStore: cache.NewStore(cache.MetaNamespaceKeyFunc), client: c}
|
2016-03-15 00:33:12 +00:00
|
|
|
}
|