Commit 120256ec authored by Matt Butcher's avatar Matt Butcher Committed by GitHub

Merge pull request #1057 from technosophos/feat/configmaps-default

feat(tiller): make configmaps the default storage
parents 63719deb 5bcf29d2
......@@ -23,6 +23,7 @@ These dependencies are expressed as interfaces so that alternate implementations
package environment
import (
"errors"
"io"
"k8s.io/helm/pkg/chartutil"
......@@ -31,11 +32,9 @@ import (
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver"
"k8s.io/kubernetes/pkg/client/unversioned"
)
// UseConfigMaps is a feature flags to toggle use of configmaps storage driver.
const UseConfigMaps = false
// TillerNamespace is the namespace tiller is running in.
const TillerNamespace = "kube-system"
......@@ -135,6 +134,9 @@ type KubeClient interface {
// reader must contain a YAML stream (one or more YAML documents separated
// by "\n---\n").
Update(namespace string, originalReader, modifiedReader io.Reader) error
// APIClient gets a raw API client for Kubernetes.
APIClient() (unversioned.Interface, error)
}
// PrintingKubeClient implements KubeClient, but simply prints the reader to
......@@ -143,6 +145,14 @@ type PrintingKubeClient struct {
Out io.Writer
}
// APIClient always returns an error.
//
// The printing client does not have access to a Kubernetes client at all. So it
// will always return an error if the client is accessed.
func (p *PrintingKubeClient) APIClient() (unversioned.Interface, error) {
return nil, errors.New("no API client found")
}
// Create prints the values of what would be created with a real KubeClient.
func (p *PrintingKubeClient) Create(ns string, r io.Reader) error {
_, err := io.Copy(p.Out, r)
......@@ -196,23 +206,9 @@ func New() *Environment {
GoTplEngine: e,
}
kbc := kube.New(nil)
var sd *storage.Storage
if UseConfigMaps {
c, err := kbc.Client()
if err != nil {
// panic because we cant initliaze driver with no client
panic(err)
}
sd = storage.Init(driver.NewConfigMaps(c.ConfigMaps(TillerNamespace)))
} else {
sd = storage.Init(driver.NewMemory())
}
return &Environment{
EngineYard: ey,
Releases: sd, //storage.Init(driver.NewMemory()),
KubeClient: kbc, //kube.New(nil), //&PrintingKubeClient{Out: os.Stdout},
Releases: storage.Init(driver.NewMemory()),
KubeClient: kube.New(nil),
}
}
......@@ -26,6 +26,8 @@ import (
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver"
unversionedclient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
)
type mockEngine struct {
......@@ -47,6 +49,10 @@ func (r *mockReleaseStorage) Create(v *release.Release) error {
return nil
}
func (r *mockReleaseStorage) Name() string {
return "mockReleaseStorage"
}
func (r *mockReleaseStorage) Get(k string) (*release.Release, error) {
return r.rel, nil
}
......@@ -81,6 +87,10 @@ func (r *mockReleaseStorage) History(n string) ([]*release.Release, error) {
type mockKubeClient struct {
}
func (k *mockKubeClient) APIClient() (unversionedclient.Interface, error) {
return testclient.NewSimpleFake(), nil
}
func (k *mockKubeClient) Create(ns string, r io.Reader) error {
return nil
}
......
......@@ -26,6 +26,13 @@ import (
"google.golang.org/grpc"
"k8s.io/helm/cmd/tiller/environment"
"k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver"
)
const (
storageMemory = "memory"
storageConfigMap = "configmap"
)
// rootServer is the root gRPC server.
......@@ -38,8 +45,11 @@ var rootServer = grpc.NewServer()
// Any changes to env should be done before rootServer.Serve() is called.
var env = environment.New()
var addr = ":44134"
var probe = ":44135"
var (
addr = ":44134"
probe = ":44135"
store = storageConfigMap
)
const globalUsage = `The Kubernetes Helm server.
......@@ -58,10 +68,22 @@ var rootCommand = &cobra.Command{
func main() {
pf := rootCommand.PersistentFlags()
pf.StringVarP(&addr, "listen", "l", ":44134", "The address:port to listen on")
pf.StringVar(&store, "storage", storageConfigMap, "The storage driver to use. One of 'configmap' or 'memory'")
rootCommand.Execute()
}
func start(c *cobra.Command, args []string) {
switch store {
case storageMemory:
env.Releases = storage.Init(driver.NewMemory())
case storageConfigMap:
c, err := env.KubeClient.APIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot initialize Kubernetes connection: %s", err)
}
env.Releases = storage.Init(driver.NewConfigMaps(c.ConfigMaps(environment.TillerNamespace)))
}
lstn, err := net.Listen("tcp", addr)
if err != nil {
fmt.Fprintf(os.Stderr, "Server died: %s\n", err)
......@@ -70,6 +92,7 @@ func start(c *cobra.Command, args []string) {
fmt.Printf("Tiller is running on %s\n", addr)
fmt.Printf("Tiller probes server is running on %s\n", probe)
fmt.Printf("Storage driver is %s\n", env.Releases.Name())
srvErrCh := make(chan error)
probeErrCh := make(chan error)
......
......@@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/batch"
unversionedclient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
......@@ -54,6 +55,16 @@ func New(config clientcmd.ClientConfig) *Client {
// ResourceActorFunc performs an action on a single resource.
type ResourceActorFunc func(*resource.Info) error
// APIClient returns a Kubernetes API client.
//
// This is necessary because cmdutil.Client is a field, not a method, which
// means it can't satisfy an interface's method requirement. In order to ensure
// that an implementation of environment.KubeClient can access the raw API client,
// it is necessary to add this method.
func (c *Client) APIClient() (unversionedclient.Interface, error) {
return c.Client()
}
// Create creates kubernetes resources from an io.reader
//
// Namespace will set the namespace
......
......@@ -32,6 +32,9 @@ import (
client "k8s.io/kubernetes/pkg/client/unversioned"
)
// ConfigMapsDriverName is the string name of the driver.
const ConfigMapsDriverName = "ConfigMap"
var b64 = base64.StdEncoding
// labels is a map of key value pairs to be included as metadata in a configmap object.
......@@ -54,6 +57,11 @@ func NewConfigMaps(impl client.ConfigMapsInterface) *ConfigMaps {
return &ConfigMaps{impl: impl}
}
// Name returns the name of the driver.
func (cfgmaps *ConfigMaps) Name() string {
return ConfigMapsDriverName
}
// Get fetches the release named by key. The corresponding release is returned
// or error if not found.
func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) {
......
......@@ -27,6 +27,15 @@ import (
"k8s.io/kubernetes/pkg/client/unversioned"
)
var _ Driver = &ConfigMaps{}
func TestConfigMapName(t *testing.T) {
c := newTestFixture(t)
if c.Name() != ConfigMapsDriverName {
t.Errorf("Expected name to be %q, got %q", ConfigMapsDriverName, c.Name())
}
}
func TestConfigMapGet(t *testing.T) {
key := "key-1"
rel := newTestRelease(key, 1, rspb.Status_DEPLOYED)
......
......@@ -73,4 +73,5 @@ type Driver interface {
Updator
Deletor
Queryor
Name() string
}
......@@ -22,6 +22,9 @@ import (
rspb "k8s.io/helm/pkg/proto/hapi/release"
)
// MemoryDriverName is the string name of this driver.
const MemoryDriverName = "Memory"
// Memory is the in-memory storage driver implementation.
type Memory struct {
sync.RWMutex
......@@ -33,6 +36,11 @@ func NewMemory() *Memory {
return &Memory{cache: map[string]*rspb.Release{}}
}
// Name returns the name of the driver.
func (mem *Memory) Name() string {
return MemoryDriverName
}
// Get returns the release named by key or returns ErrReleaseNotFound.
func (mem *Memory) Get(key string) (*rspb.Release, error) {
defer unlock(mem.rlock())
......
......@@ -23,6 +23,15 @@ import (
rspb "k8s.io/helm/pkg/proto/hapi/release"
)
var _ Driver = &Memory{}
func TestMemoryName(t *testing.T) {
mem := NewMemory()
if mem.Name() != MemoryDriverName {
t.Errorf("Expected name to be %q, got %q", MemoryDriverName, mem.Name())
}
}
func TestMemoryGet(t *testing.T) {
key := "test-1"
rls := &rspb.Release{Name: key}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment