Skip to content

Commit

Permalink
Add an external hostpath provisioner to localkube.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlorenc committed Mar 1, 2017
1 parent 0c616a6 commit e969ebe
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 21 deletions.
3 changes: 3 additions & 0 deletions cmd/localkube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,7 @@ func SetupServer(s *localkube.LocalkubeServer) {
// setup proxy
proxy := s.NewProxyServer()
s.AddServer(proxy)

storageProvisioner := s.NewStorageProvisionerServer()
s.AddServer(storageProvisioner)
}
151 changes: 151 additions & 0 deletions pkg/localkube/storage_provisioner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 localkube

import (
"errors"
"fmt"
"os"
"path"
"time"

"github.com/golang/glog"
"github.com/kubernetes-incubator/external-storage/lib/controller"
"github.com/kubernetes-incubator/external-storage/lib/leaderelection"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/pkg/types"
"k8s.io/client-go/pkg/util/uuid"
"k8s.io/client-go/pkg/util/wait"
"k8s.io/client-go/rest"
)

const (
resyncPeriod = 15 * time.Second
provisionerName = "k8s.io/minikube-hostpath"
exponentialBackOffOnError = false
failedRetryThreshold = 5
leasePeriod = leaderelection.DefaultLeaseDuration
retryPeriod = leaderelection.DefaultRetryPeriod
renewDeadline = leaderelection.DefaultRenewDeadline
termLimit = leaderelection.DefaultTermLimit
)

type hostPathProvisioner struct {
// The directory to create PV-backing directories in
pvDir string

// Identity of this hostPathProvisioner, generated. Used to identify "this"
// provisioner's PVs.
identity types.UID
}

func NewHostPathProvisioner() controller.Provisioner {
return &hostPathProvisioner{
pvDir: "/tmp/hostpath-provisioner",
identity: uuid.NewUUID(),
}
}

var _ controller.Provisioner = &hostPathProvisioner{}

// Provision creates a storage asset and returns a PV object representing it.
func (p *hostPathProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
path := path.Join(p.pvDir, options.PVName)

if err := os.MkdirAll(path, 0777); err != nil {
return nil, err
}

pv := &v1.PersistentVolume{
ObjectMeta: v1.ObjectMeta{
Name: options.PVName,
Annotations: map[string]string{
"hostPathProvisionerIdentity": string(p.identity),
},
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
AccessModes: options.PVC.Spec.AccessModes,
Capacity: v1.ResourceList{
v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
},
PersistentVolumeSource: v1.PersistentVolumeSource{
HostPath: &v1.HostPathVolumeSource{
Path: path,
},
},
},
}

return pv, nil
}

// Delete removes the storage asset that was created by Provision represented
// by the given PV.
func (p *hostPathProvisioner) Delete(volume *v1.PersistentVolume) error {
ann, ok := volume.Annotations["hostPathProvisionerIdentity"]
if !ok {
return errors.New("identity annotation not found on PV")
}
if ann != string(p.identity) {
return &controller.IgnoredError{"identity annotation on PV does not match ours"}
}

path := path.Join(p.pvDir, volume.Name)
if err := os.RemoveAll(path); err != nil {
return err
}

return nil
}

func (lk LocalkubeServer) NewStorageProvisionerServer() Server {
return NewSimpleServer("storage-provisioner", serverInterval, StartStorageProvisioner(lk))
}

func StartStorageProvisioner(lk LocalkubeServer) func() error {

// Create an InClusterConfig and use it to create a client for the controller
// to use to communicate with Kubernetes
config := rest.Config{Host: "http://localhost:8080"}
return func() error {

clientset, err := kubernetes.NewForConfig(&config)
if err != nil {
glog.Fatalf("Failed to create client: %v", err)
}

// The controller needs to know what the server version is because out-of-tree
// provisioners aren't officially supported until 1.5
serverVersion, err := clientset.Discovery().ServerVersion()
if err != nil {
return fmt.Errorf("Error getting server version: %v", err)
}

// Create the provisioner: it implements the Provisioner interface expected by
// the controller
hostPathProvisioner := NewHostPathProvisioner()

// Start the provision controller which will dynamically provision hostPath
// PVs
pc := controller.NewProvisionController(clientset, resyncPeriod, provisionerName, hostPathProvisioner, serverVersion.GitVersion, exponentialBackOffOnError, failedRetryThreshold, leasePeriod, renewDeadline, retryPeriod, termLimit)

pc.Run(wait.NeverStop)
return nil
}
}
23 changes: 11 additions & 12 deletions pkg/minikube/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,20 @@ import (
"github.com/docker/machine/libmachine"
"github.com/pkg/browser"
"github.com/pkg/errors"
"k8s.io/client-go/1.5/kubernetes"
corev1 "k8s.io/client-go/1.5/kubernetes/typed/core/v1"
kubeapi "k8s.io/client-go/1.5/pkg/api"
"k8s.io/client-go/1.5/pkg/api/v1"
"k8s.io/client-go/1.5/tools/clientcmd"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/tools/clientcmd"

"text/template"

"k8s.io/client-go/1.5/pkg/labels"
"k8s.io/client-go/pkg/labels"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/util"
)

type K8sClient interface {
GetCoreClient() (corev1.CoreInterface, error)
GetCoreClient() (corev1.CoreV1Interface, error)
}

type K8sClientGetter struct{}
Expand All @@ -52,7 +51,7 @@ func init() {
k8s = &K8sClientGetter{}
}

func (*K8sClientGetter) GetCoreClient() (corev1.CoreInterface, error) {
func (*K8sClientGetter) GetCoreClient() (corev1.CoreV1Interface, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
configOverrides := &clientcmd.ConfigOverrides{}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
Expand Down Expand Up @@ -95,7 +94,7 @@ func GetServiceURLs(api libmachine.API, namespace string, t *template.Template)

serviceInterface := client.Services(namespace)

svcs, err := serviceInterface.List(kubeapi.ListOptions{})
svcs, err := serviceInterface.List(v1.ListOptions{})
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -133,7 +132,7 @@ func GetServiceURLsForService(api libmachine.API, namespace, service string, t *
return printURLsForService(client, ip, service, namespace, t)
}

func printURLsForService(c corev1.CoreInterface, ip, service, namespace string, t *template.Template) ([]string, error) {
func printURLsForService(c corev1.CoreV1Interface, ip, service, namespace string, t *template.Template) ([]string, error) {
if t == nil {
return nil, errors.New("Error, attempted to generate service url with nil --format template")
}
Expand Down Expand Up @@ -254,7 +253,7 @@ func GetServiceListByLabel(namespace string, key string, value string) (*v1.Serv

func getServiceListFromServicesByLabel(services corev1.ServiceInterface, key string, value string) (*v1.ServiceList, error) {
selector := labels.SelectorFromSet(labels.Set(map[string]string{key: value}))
serviceList, err := services.List(kubeapi.ListOptions{LabelSelector: selector})
serviceList, err := services.List(v1.ListOptions{LabelSelector: selector.String()})
if err != nil {
return &v1.ServiceList{}, &util.RetriableError{Err: err}
}
Expand Down Expand Up @@ -320,7 +319,7 @@ func DeleteSecret(namespace, name string) error {
return &util.RetriableError{Err: err}
}

err = secrets.Delete(name, &kubeapi.DeleteOptions{})
err = secrets.Delete(name, &v1.DeleteOptions{})
if err != nil {
return &util.RetriableError{Err: err}
}
Expand Down
17 changes: 8 additions & 9 deletions pkg/minikube/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ import (
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/host"
"github.com/pkg/errors"
corev1 "k8s.io/client-go/1.5/kubernetes/typed/core/v1"
"k8s.io/client-go/1.5/kubernetes/typed/core/v1/fake"
"k8s.io/client-go/1.5/pkg/api"
"k8s.io/client-go/1.5/pkg/api/v1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/kubernetes/typed/core/v1/fake"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/tests"
)
Expand All @@ -37,14 +36,14 @@ type MockClientGetter struct {
servicesMap map[string]corev1.ServiceInterface
}

func (m *MockClientGetter) GetCoreClient() (corev1.CoreInterface, error) {
func (m *MockClientGetter) GetCoreClient() (corev1.CoreV1Interface, error) {
return &MockCoreClient{
servicesMap: m.servicesMap,
}, nil
}

type MockCoreClient struct {
fake.FakeCore
fake.FakeCoreV1
servicesMap map[string]corev1.ServiceInterface
}

Expand Down Expand Up @@ -171,12 +170,12 @@ type MockServiceInterface struct {
ServiceList *v1.ServiceList
}

func (s MockServiceInterface) List(opts api.ListOptions) (*v1.ServiceList, error) {
func (s MockServiceInterface) List(opts v1.ListOptions) (*v1.ServiceList, error) {
serviceList := &v1.ServiceList{
Items: []v1.Service{},
}
if opts.LabelSelector != nil {
keyValArr := strings.Split(opts.LabelSelector.String(), "=")
if opts.LabelSelector != "" {
keyValArr := strings.Split(opts.LabelSelector, "=")

for _, service := range s.ServiceList.Items {
if service.Spec.Selector[keyValArr[0]] == keyValArr[1] {
Expand Down

0 comments on commit e969ebe

Please sign in to comment.