mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
Provide current namespace to InClusterConfig
This commit is contained in:
parent
f0061c7105
commit
20216fa607
@ -2218,6 +2218,8 @@ const (
|
|||||||
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
||||||
// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
|
// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
|
||||||
ServiceAccountRootCAKey = "ca.crt"
|
ServiceAccountRootCAKey = "ca.crt"
|
||||||
|
// ServiceAccountNamespaceKey is the key of the optional namespace to use as the default for namespaced API calls
|
||||||
|
ServiceAccountNamespaceKey = "namespace"
|
||||||
|
|
||||||
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
||||||
//
|
//
|
||||||
|
@ -2685,6 +2685,8 @@ const (
|
|||||||
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
||||||
// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
|
// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
|
||||||
ServiceAccountRootCAKey = "ca.crt"
|
ServiceAccountRootCAKey = "ca.crt"
|
||||||
|
// ServiceAccountNamespaceKey is the key of the optional namespace to use as the default for namespaced API calls
|
||||||
|
ServiceAccountNamespaceKey = "namespace"
|
||||||
|
|
||||||
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
||||||
//
|
//
|
||||||
|
@ -19,8 +19,10 @@ package clientcmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
@ -325,12 +327,19 @@ func (inClusterClientConfig) ClientConfig() (*client.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (inClusterClientConfig) Namespace() (string, error) {
|
func (inClusterClientConfig) Namespace() (string, error) {
|
||||||
// TODO: generic way to figure out what namespace you are running in?
|
// This way assumes you've set the POD_NAMESPACE environment variable using the downward API.
|
||||||
// This way assumes you've set the POD_NAMESPACE environment variable
|
// This check has to be done first for backwards compatibility with the way InClusterConfig was originally set up
|
||||||
// using the downward API.
|
|
||||||
if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
|
if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
|
||||||
return ns, nil
|
return ns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fall back to the namespace associated with the service account token, if available
|
||||||
|
if data, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
|
||||||
|
if ns := strings.TrimSpace(string(data)); len(ns) > 0 {
|
||||||
|
return ns, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return "default", nil
|
return "default", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,6 +325,7 @@ func (e *TokensController) createSecret(serviceAccount *api.ServiceAccount) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
secret.Data[api.ServiceAccountTokenKey] = []byte(token)
|
secret.Data[api.ServiceAccountTokenKey] = []byte(token)
|
||||||
|
secret.Data[api.ServiceAccountNamespaceKey] = []byte(serviceAccount.Namespace)
|
||||||
if e.rootCA != nil && len(e.rootCA) > 0 {
|
if e.rootCA != nil && len(e.rootCA) > 0 {
|
||||||
secret.Data[api.ServiceAccountRootCAKey] = e.rootCA
|
secret.Data[api.ServiceAccountRootCAKey] = e.rootCA
|
||||||
}
|
}
|
||||||
@ -364,10 +365,12 @@ func (e *TokensController) generateTokenIfNeeded(serviceAccount *api.ServiceAcco
|
|||||||
caData := secret.Data[api.ServiceAccountRootCAKey]
|
caData := secret.Data[api.ServiceAccountRootCAKey]
|
||||||
needsCA := len(e.rootCA) > 0 && bytes.Compare(caData, e.rootCA) != 0
|
needsCA := len(e.rootCA) > 0 && bytes.Compare(caData, e.rootCA) != 0
|
||||||
|
|
||||||
|
needsNamespace := len(secret.Data[api.ServiceAccountNamespaceKey]) == 0
|
||||||
|
|
||||||
tokenData := secret.Data[api.ServiceAccountTokenKey]
|
tokenData := secret.Data[api.ServiceAccountTokenKey]
|
||||||
needsToken := len(tokenData) == 0
|
needsToken := len(tokenData) == 0
|
||||||
|
|
||||||
if !needsCA && !needsToken {
|
if !needsCA && !needsToken && !needsNamespace {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,6 +378,10 @@ func (e *TokensController) generateTokenIfNeeded(serviceAccount *api.ServiceAcco
|
|||||||
if needsCA {
|
if needsCA {
|
||||||
secret.Data[api.ServiceAccountRootCAKey] = e.rootCA
|
secret.Data[api.ServiceAccountRootCAKey] = e.rootCA
|
||||||
}
|
}
|
||||||
|
// Set the namespace
|
||||||
|
if needsNamespace {
|
||||||
|
secret.Data[api.ServiceAccountNamespaceKey] = []byte(secret.Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the token
|
// Generate the token
|
||||||
if needsToken {
|
if needsToken {
|
||||||
|
@ -115,8 +115,9 @@ func createdTokenSecret() *api.Secret {
|
|||||||
},
|
},
|
||||||
Type: api.SecretTypeServiceAccountToken,
|
Type: api.SecretTypeServiceAccountToken,
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"token": []byte("ABC"),
|
"token": []byte("ABC"),
|
||||||
"ca.crt": []byte("CA Data"),
|
"ca.crt": []byte("CA Data"),
|
||||||
|
"namespace": []byte("default"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,8 +137,9 @@ func serviceAccountTokenSecret() *api.Secret {
|
|||||||
},
|
},
|
||||||
Type: api.SecretTypeServiceAccountToken,
|
Type: api.SecretTypeServiceAccountToken,
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"token": []byte("ABC"),
|
"token": []byte("ABC"),
|
||||||
"ca.crt": []byte("CA Data"),
|
"ca.crt": []byte("CA Data"),
|
||||||
|
"namespace": []byte("default"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,6 +165,20 @@ func serviceAccountTokenSecretWithCAData(data []byte) *api.Secret {
|
|||||||
return secret
|
return secret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serviceAccountTokenSecretWithoutNamespaceData returns an existing ServiceAccountToken secret that lacks namespace data
|
||||||
|
func serviceAccountTokenSecretWithoutNamespaceData() *api.Secret {
|
||||||
|
secret := serviceAccountTokenSecret()
|
||||||
|
delete(secret.Data, api.ServiceAccountNamespaceKey)
|
||||||
|
return secret
|
||||||
|
}
|
||||||
|
|
||||||
|
// serviceAccountTokenSecretWithNamespaceData returns an existing ServiceAccountToken secret with the specified namespace data
|
||||||
|
func serviceAccountTokenSecretWithNamespaceData(data []byte) *api.Secret {
|
||||||
|
secret := serviceAccountTokenSecret()
|
||||||
|
secret.Data[api.ServiceAccountNamespaceKey] = data
|
||||||
|
return secret
|
||||||
|
}
|
||||||
|
|
||||||
func TestTokenCreation(t *testing.T) {
|
func TestTokenCreation(t *testing.T) {
|
||||||
testcases := map[string]struct {
|
testcases := map[string]struct {
|
||||||
ClientObjects []runtime.Object
|
ClientObjects []runtime.Object
|
||||||
@ -379,6 +395,24 @@ func TestTokenCreation(t *testing.T) {
|
|||||||
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"added token secret without namespace data": {
|
||||||
|
ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutNamespaceData()},
|
||||||
|
ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
|
||||||
|
|
||||||
|
AddedSecret: serviceAccountTokenSecretWithoutNamespaceData(),
|
||||||
|
ExpectedActions: []core.Action{
|
||||||
|
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"added token secret with custom namespace data": {
|
||||||
|
ClientObjects: []runtime.Object{serviceAccountTokenSecretWithNamespaceData([]byte("custom"))},
|
||||||
|
ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
|
||||||
|
|
||||||
|
AddedSecret: serviceAccountTokenSecretWithNamespaceData([]byte("custom")),
|
||||||
|
ExpectedActions: []core.Action{
|
||||||
|
// no update is performed... the custom namespace is preserved
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"updated secret without serviceaccount": {
|
"updated secret without serviceaccount": {
|
||||||
ClientObjects: []runtime.Object{serviceAccountTokenSecret()},
|
ClientObjects: []runtime.Object{serviceAccountTokenSecret()},
|
||||||
@ -422,6 +456,24 @@ func TestTokenCreation(t *testing.T) {
|
|||||||
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"updated token secret without namespace data": {
|
||||||
|
ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutNamespaceData()},
|
||||||
|
ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
|
||||||
|
|
||||||
|
UpdatedSecret: serviceAccountTokenSecretWithoutNamespaceData(),
|
||||||
|
ExpectedActions: []core.Action{
|
||||||
|
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"updated token secret with custom namespace data": {
|
||||||
|
ClientObjects: []runtime.Object{serviceAccountTokenSecretWithNamespaceData([]byte("custom"))},
|
||||||
|
ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
|
||||||
|
|
||||||
|
UpdatedSecret: serviceAccountTokenSecretWithNamespaceData([]byte("custom")),
|
||||||
|
ExpectedActions: []core.Action{
|
||||||
|
// no update is performed... the custom namespace is preserved
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"deleted secret without serviceaccount": {
|
"deleted secret without serviceaccount": {
|
||||||
DeletedSecret: serviceAccountTokenSecret(),
|
DeletedSecret: serviceAccountTokenSecret(),
|
||||||
|
@ -24,11 +24,14 @@ import (
|
|||||||
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
"k8s.io/kubernetes/pkg/util/wait"
|
"k8s.io/kubernetes/pkg/util/wait"
|
||||||
|
"k8s.io/kubernetes/pkg/version"
|
||||||
"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
|
"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var serviceAccountTokenNamespaceVersion = version.MustParse("v1.2.0")
|
||||||
|
|
||||||
var _ = Describe("ServiceAccounts", func() {
|
var _ = Describe("ServiceAccounts", func() {
|
||||||
f := NewFramework("svcaccounts")
|
f := NewFramework("svcaccounts")
|
||||||
|
|
||||||
@ -94,11 +97,28 @@ var _ = Describe("ServiceAccounts", func() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
supportsTokenNamespace, _ := serverVersionGTE(serviceAccountTokenNamespaceVersion, f.Client)
|
||||||
|
if supportsTokenNamespace {
|
||||||
|
pod.Spec.Containers = append(pod.Spec.Containers, api.Container{
|
||||||
|
Name: "namespace-test",
|
||||||
|
Image: "gcr.io/google_containers/mounttest:0.2",
|
||||||
|
Args: []string{
|
||||||
|
fmt.Sprintf("--file_content=%s/%s", serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountNamespaceKey),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
f.TestContainerOutput("consume service account token", pod, 0, []string{
|
f.TestContainerOutput("consume service account token", pod, 0, []string{
|
||||||
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountTokenKey, tokenContent),
|
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountTokenKey, tokenContent),
|
||||||
})
|
})
|
||||||
f.TestContainerOutput("consume service account root CA", pod, 1, []string{
|
f.TestContainerOutput("consume service account root CA", pod, 1, []string{
|
||||||
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountRootCAKey, rootCAContent),
|
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountRootCAKey, rootCAContent),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if supportsTokenNamespace {
|
||||||
|
f.TestContainerOutput("consume service account namespace", pod, 2, []string{
|
||||||
|
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountNamespaceKey, f.Namespace.Name),
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user