k8sclient: use InClusterConfig() for kubeclient when run as daemonset

When multus is run inside a container, eg as a daemonset, we can use
InClusterConfig() to automatically get the service account token, the
CA certificate, and the apiserver address, all without a kubeconfig
file.
This commit is contained in:
Dan Williams 2018-07-19 09:02:49 -05:00 committed by Kuralamudhan Ramakrishnan
parent 926910af08
commit 42e6894962
3 changed files with 101 additions and 61 deletions

View File

@ -17,12 +17,14 @@ package k8sclient
import (
"encoding/json"
"fmt"
"os"
"regexp"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"github.com/containernetworking/cni/libcni"
@ -53,22 +55,6 @@ func (d *defaultKubeClient) GetPod(namespace, name string) (*v1.Pod, error) {
return d.client.Core().Pods(namespace).Get(name, metav1.GetOptions{})
}
func createK8sClient(kubeconfig string) (KubeClient, error) {
// uses the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, fmt.Errorf("createK8sClient: failed to get context for the kubeconfig %v, refer Multus README.md for the usage guide: %v", kubeconfig, err)
}
// creates the clientset
client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
return &defaultKubeClient{client: client}, nil
}
func getPodNetworkAnnotation(client KubeClient, k8sArgs types.K8sArgs) (string, string, error) {
var err error
@ -276,7 +262,44 @@ type KubeClient interface {
GetPod(namespace, name string) (*v1.Pod, error)
}
func GetK8sNetwork(args *skel.CmdArgs, kubeconfig string, k8sclient KubeClient, confdir string) ([]*types.DelegateNetConf, error) {
func GetK8sClient(kubeconfig string, kubeClient KubeClient) (KubeClient, error) {
// If we get a valid kubeClient (eg from testcases) just return that
// one.
if kubeClient != nil {
return kubeClient, nil
}
var err error
var config *rest.Config
// Otherwise try to create a kubeClient from a given kubeConfig
if kubeconfig != "" {
// uses the current context in kubeconfig
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, fmt.Errorf("GetK8sClient: failed to get context for the kubeconfig %v, refer Multus README.md for the usage guide: %v", kubeconfig, err)
}
} else if os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "" {
// Try in-cluster config where multus might be running in a kubernetes pod
config, err = rest.InClusterConfig()
if err != nil {
return nil, fmt.Errorf("createK8sClient: failed to get context for in-cluster kube config, refer Multus README.md for the usage guide: %v", err)
}
} else {
// No kubernetes config; assume we shouldn't talk to Kube at all
return nil, nil
}
// creates the clientset
client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
return &defaultKubeClient{client: client}, nil
}
func GetK8sNetwork(k8sclient KubeClient, args *skel.CmdArgs, confdir string) ([]*types.DelegateNetConf, error) {
k8sArgs := types.K8sArgs{}
err := cnitypes.LoadArgs(args.Args, &k8sArgs)
@ -284,13 +307,6 @@ func GetK8sNetwork(args *skel.CmdArgs, kubeconfig string, k8sclient KubeClient,
return nil, err
}
if k8sclient == nil {
k8sclient, err = createK8sClient(kubeconfig)
if err != nil {
return nil, err
}
}
netAnnot, defaultNamespace, err := getPodNetworkAnnotation(k8sclient, k8sArgs)
if err != nil {
return nil, err

View File

@ -77,7 +77,9 @@ var _ = Describe("k8sclient operations", func() {
// net3 is not used; make sure it's not accessed
fKubeClient.AddNetConfig(fakePod.ObjectMeta.Namespace, "net3", net3)
delegates, err := GetK8sNetwork(args, "", fKubeClient, tmpDir)
kubeClient, err := GetK8sClient("", fKubeClient)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetK8sNetwork(kubeClient, args, tmpDir)
Expect(err).NotTo(HaveOccurred())
Expect(fKubeClient.PodCount).To(Equal(1))
Expect(fKubeClient.NetCount).To(Equal(2))
@ -106,7 +108,9 @@ var _ = Describe("k8sclient operations", func() {
fKubeClient.AddPod(fakePod)
fKubeClient.AddNetConfig(fakePod.ObjectMeta.Namespace, "net3", net3)
delegates, err := GetK8sNetwork(args, "", fKubeClient, tmpDir)
kubeClient, err := GetK8sClient("", fKubeClient)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetK8sNetwork(kubeClient, args, tmpDir)
Expect(len(delegates)).To(Equal(0))
Expect(err).To(MatchError("GetK8sNetwork: failed getting the delegate: getKubernetesDelegate: failed to get network resource, refer Multus README.md for the usage guide: resource not found"))
})
@ -146,7 +150,9 @@ var _ = Describe("k8sclient operations", func() {
"cniVersion": "0.2.0"
}`)
delegates, err := GetK8sNetwork(args, "", fKubeClient, tmpDir)
kubeClient, err := GetK8sClient("", fKubeClient)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetK8sNetwork(kubeClient, args, tmpDir)
Expect(err).NotTo(HaveOccurred())
Expect(fKubeClient.PodCount).To(Equal(1))
Expect(fKubeClient.NetCount).To(Equal(3))
@ -169,7 +175,9 @@ var _ = Describe("k8sclient operations", func() {
fKubeClient := testutils.NewFakeKubeClient()
fKubeClient.AddPod(fakePod)
delegates, err := GetK8sNetwork(args, "", fKubeClient, tmpDir)
kubeClient, err := GetK8sClient("", fKubeClient)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetK8sNetwork(kubeClient, args, tmpDir)
Expect(len(delegates)).To(Equal(0))
Expect(err).To(MatchError("parsePodNetworkAnnotation: failed to parse pod Network Attachment Selection Annotation JSON format: invalid character 'a' looking for beginning of value"))
})
@ -195,7 +203,9 @@ var _ = Describe("k8sclient operations", func() {
"cniVersion": "0.2.0"
}`)
delegates, err := GetK8sNetwork(args, "", fKubeClient, tmpDir)
kubeClient, err := GetK8sClient("", fKubeClient)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetK8sNetwork(kubeClient, args, tmpDir)
Expect(err).NotTo(HaveOccurred())
Expect(fKubeClient.PodCount).To(Equal(1))
Expect(fKubeClient.NetCount).To(Equal(2))
@ -244,7 +254,9 @@ var _ = Describe("k8sclient operations", func() {
net2Name := filepath.Join(tmpDir, "20-net2.conf")
fKubeClient.AddNetFile(fakePod.ObjectMeta.Namespace, "net2", net2Name, "asdfasdfasfdasfd")
delegates, err := GetK8sNetwork(args, "", fKubeClient, tmpDir)
kubeClient, err := GetK8sClient("", fKubeClient)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetK8sNetwork(kubeClient, args, tmpDir)
Expect(len(delegates)).To(Equal(0))
Expect(err).To(MatchError(fmt.Sprintf("GetK8sNetwork: failed getting the delegate: cniConfigFromNetworkResource: err in getCNIConfigFromFile: Error loading CNI config file %s: error parsing configuration: invalid character 'a' looking for beginning of value", net2Name)))
})

View File

@ -148,29 +148,52 @@ func delPlugins(exec invoke.Exec, argIfname string, delegates []*types.DelegateN
return nil
}
// Attempts to load Kubernetes-defined delegates and add them to the Multus config.
// Returns the number of Kubernetes-defined delegates added or an error.
func tryLoadK8sDelegates(args *skel.CmdArgs, conf *types.NetConf, kubeClient k8s.KubeClient) (int, error) {
var err error
kubeClient, err = k8s.GetK8sClient(conf.Kubeconfig, kubeClient)
if err != nil {
return 0, err
}
if kubeClient == nil {
if len(conf.Delegates) == 0 {
// No available kube client and no delegates, we can't do anything
return 0, fmt.Errorf("must have either Kubernetes config or delegates, refer Multus README.md for the usage guide")
}
return 0, nil
}
delegates, err := k8s.GetK8sNetwork(kubeClient, args, conf.ConfDir)
if err != nil {
if _, ok := err.(*k8s.NoK8sNetworkError); ok {
return 0, nil
}
return 0, fmt.Errorf("Multus: Err in getting k8s network from pod: %v", err)
}
if err = conf.AddDelegates(delegates); err != nil {
return 0, err
}
return len(delegates), nil
}
func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cnitypes.Result, error) {
var nopodnet bool
n, err := types.LoadNetConf(args.StdinData)
if err != nil {
return nil, fmt.Errorf("err in loading netconf: %v", err)
}
if n.Kubeconfig != "" {
delegates, err := k8s.GetK8sNetwork(args, n.Kubeconfig, kubeClient, n.ConfDir)
if err != nil {
if _, ok := err.(*k8s.NoK8sNetworkError); ok {
nopodnet = true
} else {
return nil, fmt.Errorf("Multus: Err in getting k8s network from pod: %v", err)
}
}
if err = n.AddDelegates(delegates); err != nil {
return nil, err
}
numK8sDelegates, err := tryLoadK8sDelegates(args, n, kubeClient)
if err != nil {
return nil, err
}
if n.Kubeconfig == "" || nopodnet {
if numK8sDelegates == 0 {
// cache the multus config if we have only Multus delegates
if err := saveDelegates(args.ContainerID, n.CNIDir, n.Delegates); err != nil {
return nil, fmt.Errorf("Multus: Err in saving the delegates: %v", err)
}
@ -213,29 +236,18 @@ func cmdGet(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
}
func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) error {
var nopodnet bool
in, err := types.LoadNetConf(args.StdinData)
if err != nil {
return err
}
if in.Kubeconfig != "" {
delegates, err := k8s.GetK8sNetwork(args, in.Kubeconfig, kubeClient, in.ConfDir)
if err != nil {
if _, ok := err.(*k8s.NoK8sNetworkError); ok {
nopodnet = true
} else {
return fmt.Errorf("Multus: Err in getting k8s network from pod: %v", err)
}
}
if err = in.AddDelegates(delegates); err != nil {
return err
}
numK8sDelegates, err := tryLoadK8sDelegates(args, in, kubeClient)
if err != nil {
return err
}
if in.Kubeconfig == "" || nopodnet {
if numK8sDelegates == 0 {
// re-read the scratch multus config if we have only Multus delegates
netconfBytes, err := consumeScratchNetConf(args.ContainerID, in.CNIDir)
if err != nil {
if os.IsNotExist(err) {