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 ...@@ -23,6 +23,7 @@ These dependencies are expressed as interfaces so that alternate implementations
package environment package environment
import ( import (
"errors"
"io" "io"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
...@@ -31,11 +32,9 @@ import ( ...@@ -31,11 +32,9 @@ import (
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver" "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. // TillerNamespace is the namespace tiller is running in.
const TillerNamespace = "kube-system" const TillerNamespace = "kube-system"
...@@ -135,6 +134,9 @@ type KubeClient interface { ...@@ -135,6 +134,9 @@ type KubeClient interface {
// reader must contain a YAML stream (one or more YAML documents separated // reader must contain a YAML stream (one or more YAML documents separated
// by "\n---\n"). // by "\n---\n").
Update(namespace string, originalReader, modifiedReader io.Reader) error 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 // PrintingKubeClient implements KubeClient, but simply prints the reader to
...@@ -143,6 +145,14 @@ type PrintingKubeClient struct { ...@@ -143,6 +145,14 @@ type PrintingKubeClient struct {
Out io.Writer 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. // Create prints the values of what would be created with a real KubeClient.
func (p *PrintingKubeClient) Create(ns string, r io.Reader) error { func (p *PrintingKubeClient) Create(ns string, r io.Reader) error {
_, err := io.Copy(p.Out, r) _, err := io.Copy(p.Out, r)
...@@ -196,23 +206,9 @@ func New() *Environment { ...@@ -196,23 +206,9 @@ func New() *Environment {
GoTplEngine: e, 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{ return &Environment{
EngineYard: ey, EngineYard: ey,
Releases: sd, //storage.Init(driver.NewMemory()), Releases: storage.Init(driver.NewMemory()),
KubeClient: kbc, //kube.New(nil), //&PrintingKubeClient{Out: os.Stdout}, KubeClient: kube.New(nil),
} }
} }
...@@ -26,6 +26,8 @@ import ( ...@@ -26,6 +26,8 @@ import (
"k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver" "k8s.io/helm/pkg/storage/driver"
unversionedclient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
) )
type mockEngine struct { type mockEngine struct {
...@@ -47,6 +49,10 @@ func (r *mockReleaseStorage) Create(v *release.Release) error { ...@@ -47,6 +49,10 @@ func (r *mockReleaseStorage) Create(v *release.Release) error {
return nil return nil
} }
func (r *mockReleaseStorage) Name() string {
return "mockReleaseStorage"
}
func (r *mockReleaseStorage) Get(k string) (*release.Release, error) { func (r *mockReleaseStorage) Get(k string) (*release.Release, error) {
return r.rel, nil return r.rel, nil
} }
...@@ -81,6 +87,10 @@ func (r *mockReleaseStorage) History(n string) ([]*release.Release, error) { ...@@ -81,6 +87,10 @@ func (r *mockReleaseStorage) History(n string) ([]*release.Release, error) {
type mockKubeClient struct { type mockKubeClient struct {
} }
func (k *mockKubeClient) APIClient() (unversionedclient.Interface, error) {
return testclient.NewSimpleFake(), nil
}
func (k *mockKubeClient) Create(ns string, r io.Reader) error { func (k *mockKubeClient) Create(ns string, r io.Reader) error {
return nil return nil
} }
......
...@@ -26,6 +26,13 @@ import ( ...@@ -26,6 +26,13 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
"k8s.io/helm/cmd/tiller/environment" "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. // rootServer is the root gRPC server.
...@@ -38,8 +45,11 @@ var rootServer = grpc.NewServer() ...@@ -38,8 +45,11 @@ var rootServer = grpc.NewServer()
// Any changes to env should be done before rootServer.Serve() is called. // Any changes to env should be done before rootServer.Serve() is called.
var env = environment.New() var env = environment.New()
var addr = ":44134" var (
var probe = ":44135" addr = ":44134"
probe = ":44135"
store = storageConfigMap
)
const globalUsage = `The Kubernetes Helm server. const globalUsage = `The Kubernetes Helm server.
...@@ -58,10 +68,22 @@ var rootCommand = &cobra.Command{ ...@@ -58,10 +68,22 @@ var rootCommand = &cobra.Command{
func main() { func main() {
pf := rootCommand.PersistentFlags() pf := rootCommand.PersistentFlags()
pf.StringVarP(&addr, "listen", "l", ":44134", "The address:port to listen on") 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() rootCommand.Execute()
} }
func start(c *cobra.Command, args []string) { 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) lstn, err := net.Listen("tcp", addr)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Server died: %s\n", err) fmt.Fprintf(os.Stderr, "Server died: %s\n", err)
...@@ -70,6 +92,7 @@ func start(c *cobra.Command, args []string) { ...@@ -70,6 +92,7 @@ func start(c *cobra.Command, args []string) {
fmt.Printf("Tiller is running on %s\n", addr) fmt.Printf("Tiller is running on %s\n", addr)
fmt.Printf("Tiller probes server is running on %s\n", probe) 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) srvErrCh := make(chan error)
probeErrCh := make(chan error) probeErrCh := make(chan error)
......
...@@ -29,6 +29,7 @@ import ( ...@@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
unversionedclient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
...@@ -54,6 +55,16 @@ func New(config clientcmd.ClientConfig) *Client { ...@@ -54,6 +55,16 @@ func New(config clientcmd.ClientConfig) *Client {
// ResourceActorFunc performs an action on a single resource. // ResourceActorFunc performs an action on a single resource.
type ResourceActorFunc func(*resource.Info) error 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 // Create creates kubernetes resources from an io.reader
// //
// Namespace will set the namespace // Namespace will set the namespace
......
...@@ -32,6 +32,9 @@ import ( ...@@ -32,6 +32,9 @@ import (
client "k8s.io/kubernetes/pkg/client/unversioned" client "k8s.io/kubernetes/pkg/client/unversioned"
) )
// ConfigMapsDriverName is the string name of the driver.
const ConfigMapsDriverName = "ConfigMap"
var b64 = base64.StdEncoding var b64 = base64.StdEncoding
// labels is a map of key value pairs to be included as metadata in a configmap object. // 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 { ...@@ -54,6 +57,11 @@ func NewConfigMaps(impl client.ConfigMapsInterface) *ConfigMaps {
return &ConfigMaps{impl: impl} 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 // Get fetches the release named by key. The corresponding release is returned
// or error if not found. // or error if not found.
func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) { func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) {
......
...@@ -27,6 +27,15 @@ import ( ...@@ -27,6 +27,15 @@ import (
"k8s.io/kubernetes/pkg/client/unversioned" "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) { func TestConfigMapGet(t *testing.T) {
key := "key-1" key := "key-1"
rel := newTestRelease(key, 1, rspb.Status_DEPLOYED) rel := newTestRelease(key, 1, rspb.Status_DEPLOYED)
......
...@@ -73,4 +73,5 @@ type Driver interface { ...@@ -73,4 +73,5 @@ type Driver interface {
Updator Updator
Deletor Deletor
Queryor Queryor
Name() string
} }
...@@ -22,6 +22,9 @@ import ( ...@@ -22,6 +22,9 @@ import (
rspb "k8s.io/helm/pkg/proto/hapi/release" 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. // Memory is the in-memory storage driver implementation.
type Memory struct { type Memory struct {
sync.RWMutex sync.RWMutex
...@@ -33,6 +36,11 @@ func NewMemory() *Memory { ...@@ -33,6 +36,11 @@ func NewMemory() *Memory {
return &Memory{cache: map[string]*rspb.Release{}} 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. // Get returns the release named by key or returns ErrReleaseNotFound.
func (mem *Memory) Get(key string) (*rspb.Release, error) { func (mem *Memory) Get(key string) (*rspb.Release, error) {
defer unlock(mem.rlock()) defer unlock(mem.rlock())
......
...@@ -23,6 +23,15 @@ import ( ...@@ -23,6 +23,15 @@ import (
rspb "k8s.io/helm/pkg/proto/hapi/release" 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) { func TestMemoryGet(t *testing.T) {
key := "test-1" key := "test-1"
rls := &rspb.Release{Name: key} 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