Commit 9f9b3e87 authored by Steven E. Harris's avatar Steven E. Harris Committed by Adam Reese

Use versioned API types from the client-go library (#2524)

* Use versioned API types from the client-go library

Wherever possible, use the k8s.io/client-go/kubernetes.Interface type
in favor of the client-related types from package

  k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset

The latter are still required by the kubectl "reaper" types used in
the "installer" and "kube" packages.

* Accept the default deployment replica count of one

Don't bother requesting a single replica explicitly.
parent 15c14194
......@@ -27,6 +27,7 @@ import (
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
......@@ -241,16 +242,39 @@ func prettyError(err error) error {
return errors.New(grpc.ErrorDesc(err))
}
// getKubeClient is a convenience method for creating kubernetes config and client
// for a given kubeconfig context
func getKubeClient(context string) (*rest.Config, *internalclientset.Clientset, error) {
// configForContext creates a Kubernetes REST client configuration for a given kubeconfig context.
func configForContext(context string) (*rest.Config, error) {
config, err := kube.GetConfig(context).ClientConfig()
if err != nil {
return nil, nil, fmt.Errorf("could not get kubernetes config for context '%s': %s", context, err)
return nil, fmt.Errorf("could not get Kubernetes config for context %q: %s", context, err)
}
return config, nil
}
// getKubeClient creates a Kubernetes config and client for a given kubeconfig context.
func getKubeClient(context string) (*rest.Config, kubernetes.Interface, error) {
config, err := configForContext(context)
if err != nil {
return nil, nil, err
}
client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, nil, fmt.Errorf("could not get Kubernetes client: %s", err)
}
return config, client, nil
}
// getInternalKubeClient creates a Kubernetes config and an "internal" client for a given kubeconfig context.
//
// Prefer the similar getKubeClient if you don't need to use such an internal client.
func getInternalKubeClient(context string) (*rest.Config, internalclientset.Interface, error) {
config, err := configForContext(context)
if err != nil {
return nil, nil, err
}
client, err := internalclientset.NewForConfig(config)
if err != nil {
return nil, nil, fmt.Errorf("could not get kubernetes client: %s", err)
return nil, nil, fmt.Errorf("could not get Kubernetes client: %s", err)
}
return config, client, nil
}
......
......@@ -24,7 +24,7 @@ import (
"github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/client-go/kubernetes"
"k8s.io/helm/cmd/helm/installer"
"k8s.io/helm/pkg/getter"
......@@ -77,7 +77,7 @@ type initCmd struct {
out io.Writer
home helmpath.Home
opts installer.Options
kubeClient internalclientset.Interface
kubeClient kubernetes.Interface
serviceAccount string
}
......
......@@ -30,10 +30,10 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
testcore "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/helm/cmd/helm/installer"
"k8s.io/helm/pkg/helm/helmpath"
......@@ -52,7 +52,7 @@ func TestInitCmd(t *testing.T) {
out: &buf,
home: helmpath.Home(home),
kubeClient: fc,
namespace: api.NamespaceDefault,
namespace: v1.NamespaceDefault,
}
if err := cmd.run(); err != nil {
t.Errorf("expected error: %v", err)
......@@ -81,20 +81,20 @@ func TestInitCmd_exists(t *testing.T) {
defer os.Remove(home)
var buf bytes.Buffer
fc := fake.NewSimpleClientset(&extensions.Deployment{
fc := fake.NewSimpleClientset(&v1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: api.NamespaceDefault,
Namespace: v1.NamespaceDefault,
Name: "tiller-deploy",
},
})
fc.PrependReactor("*", "*", func(action testcore.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(api.Resource("deployments"), "1")
return true, nil, apierrors.NewAlreadyExists(v1.Resource("deployments"), "1")
})
cmd := &initCmd{
out: &buf,
home: helmpath.Home(home),
kubeClient: fc,
namespace: api.NamespaceDefault,
namespace: v1.NamespaceDefault,
}
if err := cmd.run(); err != nil {
t.Errorf("expected error: %v", err)
......@@ -120,7 +120,7 @@ func TestInitCmd_clientOnly(t *testing.T) {
home: helmpath.Home(home),
kubeClient: fc,
clientOnly: true,
namespace: api.NamespaceDefault,
namespace: v1.NamespaceDefault,
}
if err := cmd.run(); err != nil {
t.Errorf("unexpected error: %v", err)
......@@ -155,7 +155,7 @@ func TestInitCmd_dryRun(t *testing.T) {
kubeClient: fc,
clientOnly: true,
dryRun: true,
namespace: api.NamespaceDefault,
namespace: v1.NamespaceDefault,
}
if err := cmd.run(); err != nil {
t.Fatal(err)
......
......@@ -23,17 +23,17 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
extensionsclient "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
)
// Install uses kubernetes client to install tiller.
//
// Returns an error if the command failed.
func Install(client internalclientset.Interface, opts *Options) error {
func Install(client kubernetes.Interface, opts *Options) error {
if err := createDeployment(client.Extensions(), opts); err != nil {
return err
}
......@@ -51,7 +51,7 @@ func Install(client internalclientset.Interface, opts *Options) error {
// Upgrade uses kubernetes client to upgrade tiller to current version.
//
// Returns an error if the command failed.
func Upgrade(client internalclientset.Interface, opts *Options) error {
func Upgrade(client kubernetes.Interface, opts *Options) error {
obj, err := client.Extensions().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
if err != nil {
return err
......@@ -79,19 +79,19 @@ func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options)
}
// deployment gets the deployment object that installs Tiller.
func deployment(opts *Options) *extensions.Deployment {
func deployment(opts *Options) *v1beta1.Deployment {
return generateDeployment(opts)
}
// createService creates the Tiller service resource
func createService(client internalversion.ServicesGetter, namespace string) error {
func createService(client corev1.ServicesGetter, namespace string) error {
obj := service(namespace)
_, err := client.Services(obj.Namespace).Create(obj)
return err
}
// service gets the service object that installs Tiller.
func service(namespace string) *api.Service {
func service(namespace string) *v1.Service {
return generateService(namespace)
}
......@@ -117,36 +117,35 @@ func generateLabels(labels map[string]string) map[string]string {
return labels
}
func generateDeployment(opts *Options) *extensions.Deployment {
func generateDeployment(opts *Options) *v1beta1.Deployment {
labels := generateLabels(map[string]string{"name": "tiller"})
d := &extensions.Deployment{
d := &v1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: opts.Namespace,
Name: deploymentName,
Labels: labels,
},
Spec: extensions.DeploymentSpec{
Replicas: 1,
Template: api.PodTemplateSpec{
Spec: v1beta1.DeploymentSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: api.PodSpec{
Spec: v1.PodSpec{
ServiceAccountName: opts.ServiceAccount,
Containers: []api.Container{
Containers: []v1.Container{
{
Name: "tiller",
Image: opts.selectImage(),
ImagePullPolicy: opts.pullPolicy(),
Ports: []api.ContainerPort{
Ports: []v1.ContainerPort{
{ContainerPort: 44134, Name: "tiller"},
},
Env: []api.EnvVar{
Env: []v1.EnvVar{
{Name: "TILLER_NAMESPACE", Value: opts.Namespace},
},
LivenessProbe: &api.Probe{
Handler: api.Handler{
HTTPGet: &api.HTTPGetAction{
LivenessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/liveness",
Port: intstr.FromInt(44135),
},
......@@ -154,9 +153,9 @@ func generateDeployment(opts *Options) *extensions.Deployment {
InitialDelaySeconds: 1,
TimeoutSeconds: 1,
},
ReadinessProbe: &api.Probe{
Handler: api.Handler{
HTTPGet: &api.HTTPGetAction{
ReadinessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/readiness",
Port: intstr.FromInt(44135),
},
......@@ -166,12 +165,10 @@ func generateDeployment(opts *Options) *extensions.Deployment {
},
},
},
HostNetwork: opts.EnableHostNetwork,
NodeSelector: map[string]string{
"beta.kubernetes.io/os": "linux",
},
SecurityContext: &api.PodSecurityContext{
HostNetwork: opts.EnableHostNetwork,
},
},
},
},
......@@ -186,22 +183,22 @@ func generateDeployment(opts *Options) *extensions.Deployment {
}
// Mount secret to "/etc/certs"
d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, api.VolumeMount{
d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, v1.VolumeMount{
Name: "tiller-certs",
ReadOnly: true,
MountPath: certsDir,
})
// Add environment variable required for enabling TLS
d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, []api.EnvVar{
d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, []v1.EnvVar{
{Name: "TILLER_TLS_VERIFY", Value: tlsVerify},
{Name: "TILLER_TLS_ENABLE", Value: tlsEnable},
{Name: "TILLER_TLS_CERTS", Value: certsDir},
}...)
// Add secret volume to deployment
d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, api.Volume{
d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, v1.Volume{
Name: "tiller-certs",
VolumeSource: api.VolumeSource{
Secret: &api.SecretVolumeSource{
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: "tiller-secret",
},
},
......@@ -210,17 +207,17 @@ func generateDeployment(opts *Options) *extensions.Deployment {
return d
}
func generateService(namespace string) *api.Service {
func generateService(namespace string) *v1.Service {
labels := generateLabels(map[string]string{"name": "tiller"})
s := &api.Service{
s := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: serviceName,
Labels: labels,
},
Spec: api.ServiceSpec{
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{
Spec: v1.ServiceSpec{
Type: v1.ServiceTypeClusterIP,
Ports: []v1.ServicePort{
{
Name: "tiller",
Port: 44134,
......@@ -244,7 +241,7 @@ func SecretManifest(opts *Options) (string, error) {
}
// createSecret creates the Tiller secret resource.
func createSecret(client internalversion.SecretsGetter, opts *Options) error {
func createSecret(client corev1.SecretsGetter, opts *Options) error {
o, err := generateSecret(opts)
if err != nil {
return err
......@@ -254,12 +251,12 @@ func createSecret(client internalversion.SecretsGetter, opts *Options) error {
}
// generateSecret builds the secret object that hold Tiller secrets.
func generateSecret(opts *Options) (*api.Secret, error) {
func generateSecret(opts *Options) (*v1.Secret, error) {
const secretName = "tiller-secret"
labels := generateLabels(map[string]string{"name": "tiller"})
secret := &api.Secret{
Type: api.SecretTypeOpaque,
secret := &v1.Secret{
Type: v1.SecretTypeOpaque,
Data: make(map[string][]byte),
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
......
This diff is collapsed.
......@@ -19,8 +19,8 @@ package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"fmt"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/helm/pkg/version"
"k8s.io/kubernetes/pkg/api"
)
const defaultImage = "gcr.io/kubernetes-helm/tiller"
......@@ -84,11 +84,11 @@ func (opts *Options) selectImage() string {
}
}
func (opts *Options) pullPolicy() api.PullPolicy {
func (opts *Options) pullPolicy() v1.PullPolicy {
if opts.UseCanary {
return api.PullAlways
return v1.PullAlways
}
return api.PullIfNotPresent
return v1.PullIfNotPresent
}
func (opts *Options) tls() bool { return opts.EnableTLS || opts.VerifyTLS }
......@@ -19,9 +19,9 @@ package installer // import "k8s.io/helm/cmd/helm/installer"
import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/client-go/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/kubectl"
)
......@@ -39,7 +39,7 @@ func Uninstall(client internalclientset.Interface, opts *Options) error {
}
// deleteService deletes the Tiller Service resource
func deleteService(client internalversion.ServicesGetter, namespace string) error {
func deleteService(client coreclient.ServicesGetter, namespace string) error {
err := client.Services(namespace).Delete(serviceName, &metav1.DeleteOptions{})
return ingoreNotFound(err)
}
......
......@@ -86,7 +86,7 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
// runReset uninstalls tiller from Kubernetes Cluster and deletes local config
func (d *resetCmd) run() error {
if d.kubeClient == nil {
_, c, err := getKubeClient(kubeContext)
_, c, err := getInternalKubeClient(kubeContext)
if err != nil {
return fmt.Errorf("could not get kubernetes client: %s", err)
}
......
/*
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 portforwarder
import (
"k8s.io/client-go/pkg/api/v1"
)
// These functions are adapted from the "kubernetes" repository's file
//
// kubernetes/pkg/api/v1/pod/util.go
//
// where they rely upon the API types specific to that repository. Here we recast them to operate
// upon the type from the "client-go" repository instead.
// isPodReady returns true if a pod is ready; false otherwise.
func isPodReady(pod *v1.Pod) bool {
return isPodReadyConditionTrue(pod.Status)
}
// isPodReady retruns true if a pod is ready; false otherwise.
func isPodReadyConditionTrue(status v1.PodStatus) bool {
condition := getPodReadyCondition(status)
return condition != nil && condition.Status == v1.ConditionTrue
}
// getPodReadyCondition extracts the pod ready condition from the given status and returns that.
// Returns nil if the condition is not present.
func getPodReadyCondition(status v1.PodStatus) *v1.PodCondition {
_, condition := getPodCondition(&status, v1.PodReady)
return condition
}
// getPodCondition extracts the provided condition from the given status and returns that.
// Returns nil and -1 if the condition is not present, and the index of the located condition.
func getPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
if status == nil {
return -1, nil
}
for i := range status.Conditions {
if status.Conditions[i].Type == conditionType {
return i, &status.Conditions[i]
}
}
return -1, nil
}
......@@ -21,17 +21,17 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"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/rest"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/helm/pkg/kube"
)
// New creates a new and initialized tunnel.
func New(namespace string, client *internalclientset.Clientset, config *rest.Config) (*kube.Tunnel, error) {
podName, err := getTillerPodName(client.Core(), namespace)
func New(namespace string, client kubernetes.Interface, config *rest.Config) (*kube.Tunnel, error) {
podName, err := getTillerPodName(client.CoreV1(), namespace)
if err != nil {
return nil, err
}
......@@ -40,7 +40,7 @@ func New(namespace string, client *internalclientset.Clientset, config *rest.Con
return t, t.ForwardPort()
}
func getTillerPodName(client internalversion.PodsGetter, namespace string) (string, error) {
func getTillerPodName(client corev1.PodsGetter, namespace string) (string, error) {
// TODO use a const for labels
selector := labels.Set{"app": "helm", "name": "tiller"}.AsSelector()
pod, err := getFirstRunningPod(client, namespace, selector)
......@@ -50,7 +50,7 @@ func getTillerPodName(client internalversion.PodsGetter, namespace string) (stri
return pod.ObjectMeta.GetName(), nil
}
func getFirstRunningPod(client internalversion.PodsGetter, namespace string, selector labels.Selector) (*api.Pod, error) {
func getFirstRunningPod(client corev1.PodsGetter, namespace string, selector labels.Selector) (*v1.Pod, error) {
options := metav1.ListOptions{LabelSelector: selector.String()}
pods, err := client.Pods(namespace).List(options)
if err != nil {
......@@ -60,7 +60,7 @@ func getFirstRunningPod(client internalversion.PodsGetter, namespace string, sel
return nil, fmt.Errorf("could not find tiller")
}
for _, p := range pods.Items {
if api.IsPodReady(&p) {
if isPodReady(&p) {
return &p, nil
}
}
......
......@@ -20,63 +20,63 @@ import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/pkg/api/v1"
)
func mockTillerPod() api.Pod {
return api.Pod{
func mockTillerPod() v1.Pod {
return v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "orca",
Namespace: api.NamespaceDefault,
Namespace: v1.NamespaceDefault,
Labels: map[string]string{"app": "helm", "name": "tiller"},
},
Status: api.PodStatus{
Phase: api.PodRunning,
Conditions: []api.PodCondition{
Status: v1.PodStatus{
Phase: v1.PodRunning,
Conditions: []v1.PodCondition{
{
Status: api.ConditionTrue,
Type: api.PodReady,
Status: v1.ConditionTrue,
Type: v1.PodReady,
},
},
},
}
}
func mockTillerPodPending() api.Pod {
func mockTillerPodPending() v1.Pod {
p := mockTillerPod()
p.Name = "blue"
p.Status.Conditions[0].Status = api.ConditionFalse
p.Status.Conditions[0].Status = v1.ConditionFalse
return p
}
func TestGetFirstPod(t *testing.T) {
tests := []struct {
name string
pods []api.Pod
pods []v1.Pod
expected string
err bool
}{
{
name: "with a ready pod",
pods: []api.Pod{mockTillerPod()},
pods: []v1.Pod{mockTillerPod()},
expected: "orca",
},
{
name: "without a ready pod",
pods: []api.Pod{mockTillerPodPending()},
pods: []v1.Pod{mockTillerPodPending()},
err: true,
},
{
name: "without a pod",
pods: []api.Pod{},
pods: []v1.Pod{},
err: true,
},
}
for _, tt := range tests {
client := fake.NewSimpleClientset(&api.PodList{Items: tt.pods})
name, err := getTillerPodName(client.Core(), api.NamespaceDefault)
client := fake.NewSimpleClientset(&v1.PodList{Items: tt.pods})
name, err := getTillerPodName(client.Core(), v1.NamespaceDefault)
if (err != nil) != tt.err {
t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err)
}
......
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