kubeadm: Aggregate the token functionality in sane packages.

- Factor out token constants to kubeadmconstants. - Move cmd/kubeadm/app/util/{,token/}tokens.go - Use the token-id, token-secret, etc constants provided by the bootstrapapi package - Move cmd/kubeadm/app/master/tokens.go to cmd/kubeadm/app/phases/token/csv.go This refactor basically makes it possible to hook up kubeadm to the BootstrapSigner controller later on
parent 2509ab0c
...@@ -41,6 +41,7 @@ filegroup( ...@@ -41,6 +41,7 @@ filegroup(
"//cmd/kubeadm/app/phases/apiconfig:all-srcs", "//cmd/kubeadm/app/phases/apiconfig:all-srcs",
"//cmd/kubeadm/app/phases/certs:all-srcs", "//cmd/kubeadm/app/phases/certs:all-srcs",
"//cmd/kubeadm/app/phases/kubeconfig:all-srcs", "//cmd/kubeadm/app/phases/kubeconfig:all-srcs",
"//cmd/kubeadm/app/phases/token:all-srcs",
"//cmd/kubeadm/app/preflight:all-srcs", "//cmd/kubeadm/app/preflight:all-srcs",
"//cmd/kubeadm/app/util:all-srcs", "//cmd/kubeadm/app/util:all-srcs",
], ],
......
...@@ -33,8 +33,10 @@ go_library( ...@@ -33,8 +33,10 @@ go_library(
"//cmd/kubeadm/app/phases/apiconfig:go_default_library", "//cmd/kubeadm/app/phases/apiconfig:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library", "//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library", "//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/phases/token:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library", "//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/token:go_default_library",
"//pkg/bootstrap/api:go_default_library", "//pkg/bootstrap/api:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
......
...@@ -25,6 +25,7 @@ import ( ...@@ -25,6 +25,7 @@ import (
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
"github.com/blang/semver" "github.com/blang/semver"
) )
...@@ -75,13 +76,13 @@ func setInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error { ...@@ -75,13 +76,13 @@ func setInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error {
// Validate token if any, otherwise generate // Validate token if any, otherwise generate
if cfg.Discovery.Token != nil { if cfg.Discovery.Token != nil {
if cfg.Discovery.Token.ID != "" && cfg.Discovery.Token.Secret != "" { if cfg.Discovery.Token.ID != "" && cfg.Discovery.Token.Secret != "" {
fmt.Printf("[init] A token has been provided, validating [%s]\n", kubeadmutil.BearerToken(cfg.Discovery.Token)) fmt.Printf("[init] A token has been provided, validating [%s]\n", tokenutil.BearerToken(cfg.Discovery.Token))
if valid, err := kubeadmutil.ValidateToken(cfg.Discovery.Token); valid == false { if valid, err := tokenutil.ValidateToken(cfg.Discovery.Token); valid == false {
return err return err
} }
} else { } else {
fmt.Println("[init] A token has not been provided, generating one") fmt.Println("[init] A token has not been provided, generating one")
if err := kubeadmutil.GenerateToken(cfg.Discovery.Token); err != nil { if err := tokenutil.GenerateToken(cfg.Discovery.Token); err != nil {
return err return err
} }
} }
......
...@@ -38,8 +38,10 @@ import ( ...@@ -38,8 +38,10 @@ import (
apiconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/apiconfig" apiconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/apiconfig"
certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs" certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/token"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight" "k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
) )
var ( var (
...@@ -206,7 +208,7 @@ func (i *Init) Run(out io.Writer) error { ...@@ -206,7 +208,7 @@ func (i *Init) Run(out io.Writer) error {
// TODO: It's not great to have an exception for token here, but necessary because the apiserver doesn't handle this properly in the API yet // TODO: It's not great to have an exception for token here, but necessary because the apiserver doesn't handle this properly in the API yet
// but relies on files on disk for now, which is daunting. // but relies on files on disk for now, which is daunting.
if i.cfg.Discovery.Token != nil { if i.cfg.Discovery.Token != nil {
if err := kubemaster.CreateTokenAuthFile(kubeadmutil.BearerToken(i.cfg.Discovery.Token)); err != nil { if err := tokenphase.CreateTokenAuthFile(tokenutil.BearerToken(i.cfg.Discovery.Token)); err != nil {
return err return err
} }
} }
...@@ -235,7 +237,19 @@ func (i *Init) Run(out io.Writer) error { ...@@ -235,7 +237,19 @@ func (i *Init) Run(out io.Writer) error {
} }
} }
// PHASE 4: Set up various things in the API // PHASE 4: Set up the bootstrap tokens
if i.cfg.Discovery.Token != nil {
fmt.Printf("[token-discovery] Using token: %s\n", tokenutil.BearerToken(i.cfg.Discovery.Token))
if err := kubemaster.CreateDiscoveryDeploymentAndSecret(i.cfg, client); err != nil {
return err
}
if err := tokenphase.UpdateOrCreateToken(client, i.cfg.Discovery.Token, kubeadmconstants.DefaultTokenDuration); err != nil {
return err
}
}
// PHASE 5: Install and deploy all addons, and configure things as necessary
// Create the necessary ServiceAccounts // Create the necessary ServiceAccounts
err = apiconfigphase.CreateServiceAccounts(client) err = apiconfigphase.CreateServiceAccounts(client)
if err != nil { if err != nil {
...@@ -249,17 +263,6 @@ func (i *Init) Run(out io.Writer) error { ...@@ -249,17 +263,6 @@ func (i *Init) Run(out io.Writer) error {
} }
} }
if i.cfg.Discovery.Token != nil {
fmt.Printf("[token-discovery] Using token: %s\n", kubeadmutil.BearerToken(i.cfg.Discovery.Token))
if err := kubemaster.CreateDiscoveryDeploymentAndSecret(i.cfg, client); err != nil {
return err
}
if err := kubeadmutil.UpdateOrCreateToken(client, i.cfg.Discovery.Token, kubeadmutil.DefaultTokenDuration); err != nil {
return err
}
}
// PHASE 5: Deploy essential addons
if err := addonsphase.CreateEssentialAddons(i.cfg, client); err != nil { if err := addonsphase.CreateEssentialAddons(i.cfg, client); err != nil {
return err return err
} }
......
...@@ -31,9 +31,12 @@ import ( ...@@ -31,9 +31,12 @@ import (
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/pkg/api" "k8s.io/client-go/pkg/api"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master" kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/token"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api" bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
) )
...@@ -69,7 +72,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command { ...@@ -69,7 +72,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
}, },
} }
createCmd.PersistentFlags().DurationVar(&tokenDuration, createCmd.PersistentFlags().DurationVar(&tokenDuration,
"ttl", kubeadmutil.DefaultTokenDuration, "The duration before the token is automatically deleted.") "ttl", kubeadmconstants.DefaultTokenDuration, "The duration before the token is automatically deleted. 0 means 'never expires'.")
createCmd.PersistentFlags().StringVar( createCmd.PersistentFlags().StringVar(
&token, "token", "", &token, "token", "",
"Shared secret used to secure cluster bootstrap. If none is provided, one will be generated for you.", "Shared secret used to secure cluster bootstrap. If none is provided, one will be generated for you.",
...@@ -133,29 +136,29 @@ func RunCreateToken(out io.Writer, cmd *cobra.Command, tokenDuration time.Durati ...@@ -133,29 +136,29 @@ func RunCreateToken(out io.Writer, cmd *cobra.Command, tokenDuration time.Durati
return err return err
} }
parsedID, parsedSecret, err := kubeadmutil.ParseToken(token) parsedID, parsedSecret, err := tokenutil.ParseToken(token)
if err != nil { if err != nil {
return err return err
} }
td := &kubeadmapi.TokenDiscovery{ID: parsedID, Secret: parsedSecret} td := &kubeadmapi.TokenDiscovery{ID: parsedID, Secret: parsedSecret}
err = kubeadmutil.UpdateOrCreateToken(client, td, tokenDuration) err = tokenphase.UpdateOrCreateToken(client, td, tokenDuration)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintln(out, kubeadmutil.BearerToken(td)) fmt.Fprintln(out, tokenutil.BearerToken(td))
return nil return nil
} }
func RunGenerateToken(out io.Writer) error { func RunGenerateToken(out io.Writer) error {
td := &kubeadmapi.TokenDiscovery{} td := &kubeadmapi.TokenDiscovery{}
err := kubeadmutil.GenerateToken(td) err := tokenutil.GenerateToken(td)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintln(out, kubeadmutil.BearerToken(td)) fmt.Fprintln(out, tokenutil.BearerToken(td))
return nil return nil
} }
...@@ -183,31 +186,31 @@ func RunListTokens(out io.Writer, errW io.Writer, cmd *cobra.Command) error { ...@@ -183,31 +186,31 @@ func RunListTokens(out io.Writer, errW io.Writer, cmd *cobra.Command) error {
w := tabwriter.NewWriter(out, 10, 4, 3, ' ', 0) w := tabwriter.NewWriter(out, 10, 4, 3, ' ', 0)
fmt.Fprintln(w, "ID\tTOKEN\tTTL") fmt.Fprintln(w, "ID\tTOKEN\tTTL")
for _, secret := range results.Items { for _, secret := range results.Items {
tokenId, ok := secret.Data["token-id"] tokenId, ok := secret.Data[bootstrapapi.BootstrapTokenIDKey]
if !ok { if !ok {
fmt.Fprintf(errW, "[token] bootstrap token has no token-id data: %s\n", secret.Name) fmt.Fprintf(errW, "[token] bootstrap token has no token-id data: %s\n", secret.Name)
continue continue
} }
tokenSecret, ok := secret.Data["token-secret"] tokenSecret, ok := secret.Data[bootstrapapi.BootstrapTokenSecretKey]
if !ok { if !ok {
fmt.Fprintf(errW, "[token] bootstrap token has no token-secret data: %s\n", secret.Name) fmt.Fprintf(errW, "[token] bootstrap token has no token-secret data: %s\n", secret.Name)
continue continue
} }
token := fmt.Sprintf("%s.%s", tokenId, tokenSecret) td := &kubeadmapi.TokenDiscovery{ID: string(tokenId), Secret: string(tokenSecret)}
// Expiration time is optional, if not specified this implies the token // Expiration time is optional, if not specified this implies the token
// never expires. // never expires.
expires := "<never>" expires := "<never>"
secretExpiration, ok := secret.Data["expiration"] secretExpiration, ok := secret.Data[bootstrapapi.BootstrapTokenExpirationKey]
if ok { if ok {
expireTime, err := time.Parse(time.RFC3339, string(secretExpiration)) expireTime, err := time.Parse(time.RFC3339, string(secretExpiration))
if err != nil { if err != nil {
return fmt.Errorf("error parsing expiry time [%v]", err) return fmt.Errorf("error parsing expiration time [%v]", err)
} }
expires = kubectl.ShortHumanDuration(expireTime.Sub(time.Now())) expires = kubectl.ShortHumanDuration(expireTime.Sub(time.Now()))
} }
fmt.Fprintf(w, "%s\t%s\t%s\n", tokenId, token, expires) fmt.Fprintf(w, "%s\t%s\t%s\n", tokenId, tokenutil.BearerToken(td), expires)
} }
w.Flush() w.Flush()
...@@ -216,7 +219,7 @@ func RunListTokens(out io.Writer, errW io.Writer, cmd *cobra.Command) error { ...@@ -216,7 +219,7 @@ func RunListTokens(out io.Writer, errW io.Writer, cmd *cobra.Command) error {
// RunDeleteToken removes a bootstrap token from the server. // RunDeleteToken removes a bootstrap token from the server.
func RunDeleteToken(out io.Writer, cmd *cobra.Command, tokenId string) error { func RunDeleteToken(out io.Writer, cmd *cobra.Command, tokenId string) error {
if err := kubeadmutil.ParseTokenID(tokenId); err != nil { if err := tokenutil.ParseTokenID(tokenId); err != nil {
return err return err
} }
...@@ -225,7 +228,7 @@ func RunDeleteToken(out io.Writer, cmd *cobra.Command, tokenId string) error { ...@@ -225,7 +228,7 @@ func RunDeleteToken(out io.Writer, cmd *cobra.Command, tokenId string) error {
return err return err
} }
tokenSecretName := fmt.Sprintf("%s%s", kubeadmutil.BootstrapTokenSecretPrefix, tokenId) tokenSecretName := fmt.Sprintf("%s%s", kubeadmconstants.BootstrapTokenSecretPrefix, tokenId)
if err := client.Secrets(metav1.NamespaceSystem).Delete(tokenSecretName, nil); err != nil { if err := client.Secrets(metav1.NamespaceSystem).Delete(tokenSecretName, nil); err != nil {
return fmt.Errorf("failed to delete bootstrap token [%v]", err) return fmt.Errorf("failed to delete bootstrap token [%v]", err)
} }
......
...@@ -59,4 +59,18 @@ const ( ...@@ -59,4 +59,18 @@ const (
// Minimum amount of nodes the Service subnet should allow. // Minimum amount of nodes the Service subnet should allow.
// We need at least ten, because the DNS service is always at the tenth cluster clusterIP // We need at least ten, because the DNS service is always at the tenth cluster clusterIP
MinimumAddressesInServiceSubnet = 10 MinimumAddressesInServiceSubnet = 10
// DefaultTokenDuration specifies the default amount of time that a bootstrap token will be valid
DefaultTokenDuration = time.Duration(8) * time.Hour
// BootstrapTokenSecretPrefix the the prefix that will be used for the Secrets that are created as type bootstrap.kubernetes.io/token
BootstrapTokenSecretPrefix = "bootstrap-token-"
// CSVTokenBootstrapUser is currently the user the bootstrap token in the .csv file
// TODO: This should change to something more official and supported
// TODO: Prefix with kubeadm prefix
CSVTokenBootstrapUser = "kubeadm-node-csr"
// CSVTokenBootstrapGroup specifies the group the tokens in the .csv file will belong to
CSVTokenBootstrapGroup = "kubeadm:kubelet-bootstrap"
// The file name of the tokens file that can be used for bootstrapping
CSVTokenFileName = "tokens.csv"
) )
...@@ -21,7 +21,7 @@ go_library( ...@@ -21,7 +21,7 @@ go_library(
"//cmd/kubeadm/app/discovery/https:go_default_library", "//cmd/kubeadm/app/discovery/https:go_default_library",
"//cmd/kubeadm/app/discovery/token:go_default_library", "//cmd/kubeadm/app/discovery/token:go_default_library",
"//cmd/kubeadm/app/node:go_default_library", "//cmd/kubeadm/app/node:go_default_library",
"//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util/token:go_default_library",
"//vendor:github.com/spf13/pflag", "//vendor:github.com/spf13/pflag",
"//vendor:k8s.io/client-go/tools/clientcmd", "//vendor:k8s.io/client-go/tools/clientcmd",
"//vendor:k8s.io/client-go/tools/clientcmd/api", "//vendor:k8s.io/client-go/tools/clientcmd/api",
......
...@@ -25,7 +25,7 @@ import ( ...@@ -25,7 +25,7 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubenode "k8s.io/kubernetes/cmd/kubeadm/app/node" kubenode "k8s.io/kubernetes/cmd/kubeadm/app/node"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
) )
// For identifies and executes the desired discovery mechanism. // For identifies and executes the desired discovery mechanism.
...@@ -65,7 +65,7 @@ func runHTTPSDiscovery(hd *kubeadmapi.HTTPSDiscovery) (*clientcmdapi.Config, err ...@@ -65,7 +65,7 @@ func runHTTPSDiscovery(hd *kubeadmapi.HTTPSDiscovery) (*clientcmdapi.Config, err
// runTokenDiscovery executes token-based discovery. // runTokenDiscovery executes token-based discovery.
func runTokenDiscovery(td *kubeadmapi.TokenDiscovery) (*clientcmdapi.Config, error) { func runTokenDiscovery(td *kubeadmapi.TokenDiscovery) (*clientcmdapi.Config, error) {
if valid, err := kubeadmutil.ValidateToken(td); valid == false { if valid, err := tokenutil.ValidateToken(td); valid == false {
return nil, err return nil, err
} }
......
...@@ -16,7 +16,6 @@ go_library( ...@@ -16,7 +16,6 @@ go_library(
"manifests.go", "manifests.go",
"selfhosted.go", "selfhosted.go",
"templates.go", "templates.go",
"tokens.go",
], ],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
...@@ -31,7 +30,6 @@ go_library( ...@@ -31,7 +30,6 @@ go_library(
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/intstr", "//vendor:k8s.io/apimachinery/pkg/util/intstr",
"//vendor:k8s.io/apimachinery/pkg/util/uuid",
"//vendor:k8s.io/apimachinery/pkg/util/wait", "//vendor:k8s.io/apimachinery/pkg/util/wait",
"//vendor:k8s.io/client-go/kubernetes", "//vendor:k8s.io/client-go/kubernetes",
"//vendor:k8s.io/client-go/pkg/api", "//vendor:k8s.io/client-go/pkg/api",
......
...@@ -385,7 +385,7 @@ func getControllerManagerCommand(cfg *kubeadmapi.MasterConfiguration, selfHosted ...@@ -385,7 +385,7 @@ func getControllerManagerCommand(cfg *kubeadmapi.MasterConfiguration, selfHosted
"--service-account-private-key-file="+getCertFilePath(kubeadmconstants.ServiceAccountPrivateKeyName), "--service-account-private-key-file="+getCertFilePath(kubeadmconstants.ServiceAccountPrivateKeyName),
"--cluster-signing-cert-file="+getCertFilePath(kubeadmconstants.CACertName), "--cluster-signing-cert-file="+getCertFilePath(kubeadmconstants.CACertName),
"--cluster-signing-key-file="+getCertFilePath(kubeadmconstants.CAKeyName), "--cluster-signing-key-file="+getCertFilePath(kubeadmconstants.CAKeyName),
"--insecure-experimental-approve-all-kubelet-csrs-for-group="+KubeletBootstrapGroup, "--insecure-experimental-approve-all-kubelet-csrs-for-group="+kubeadmconstants.CSVTokenBootstrapGroup,
) )
if cfg.CloudProvider != "" { if cfg.CloudProvider != "" {
......
...@@ -19,7 +19,7 @@ go_library( ...@@ -19,7 +19,7 @@ go_library(
deps = [ deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library", "//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util/token:go_default_library",
"//pkg/kubelet/util/csr:go_default_library", "//pkg/kubelet/util/csr:go_default_library",
"//vendor:github.com/square/go-jose", "//vendor:github.com/square/go-jose",
"//vendor:k8s.io/apimachinery/pkg/types", "//vendor:k8s.io/apimachinery/pkg/types",
......
...@@ -30,7 +30,7 @@ import ( ...@@ -30,7 +30,7 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
) )
// retryTimeout between the subsequent attempts to connect // retryTimeout between the subsequent attempts to connect
...@@ -62,7 +62,7 @@ func EstablishMasterConnection(c *kubeadmapi.TokenDiscovery, clusterInfo *kubead ...@@ -62,7 +62,7 @@ func EstablishMasterConnection(c *kubeadmapi.TokenDiscovery, clusterInfo *kubead
var once sync.Once var once sync.Once
var wg sync.WaitGroup var wg sync.WaitGroup
for _, endpoint := range endpoints { for _, endpoint := range endpoints {
ac, err := createClients(caCert, endpoint, kubeadmutil.BearerToken(c), nodeName) ac, err := createClients(caCert, endpoint, tokenutil.BearerToken(c), nodeName)
if err != nil { if err != nil {
fmt.Printf("[bootstrap] Warning: %s. Skipping endpoint %s\n", err, endpoint) fmt.Printf("[bootstrap] Warning: %s. Skipping endpoint %s\n", err, endpoint)
continue continue
......
...@@ -16,7 +16,6 @@ go_library( ...@@ -16,7 +16,6 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/master:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors", "//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/types", "//vendor:k8s.io/apimachinery/pkg/types",
......
...@@ -24,7 +24,6 @@ import ( ...@@ -24,7 +24,6 @@ import (
"k8s.io/client-go/pkg/api/v1" "k8s.io/client-go/pkg/api/v1"
rbac "k8s.io/client-go/pkg/apis/rbac/v1beta1" rbac "k8s.io/client-go/pkg/apis/rbac/v1beta1"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/master"
) )
const ( const (
...@@ -117,7 +116,7 @@ func CreateClusterRoleBindings(clientset *clientset.Clientset) error { ...@@ -117,7 +116,7 @@ func CreateClusterRoleBindings(clientset *clientset.Clientset) error {
Subjects: []rbac.Subject{ Subjects: []rbac.Subject{
{ {
Kind: "Group", Kind: "Group",
Name: master.KubeletBootstrapGroup, Name: kubeadmconstants.CSVTokenBootstrapGroup,
}, },
}, },
}, },
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"bootstrap_test.go",
"csv_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"bootstrap.go",
"csv.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/util/token:go_default_library",
"//pkg/bootstrap/api:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/util/uuid",
"//vendor:k8s.io/client-go/kubernetes",
"//vendor:k8s.io/client-go/pkg/api/v1",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
/*
Copyright 2017 The Kubernetes Authors.
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 token
import (
"fmt"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/pkg/api/v1"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
)
const (
tokenCreateRetries = 5
)
// UpdateOrCreateToken attempts to update a token with the given ID, or create if it does
// not already exist.
func UpdateOrCreateToken(client *clientset.Clientset, d *kubeadmapi.TokenDiscovery, tokenDuration time.Duration) error {
// Let's make sure the token is valid
if valid, err := tokenutil.ValidateToken(d); !valid {
return err
}
secretName := fmt.Sprintf("%s%s", kubeadmconstants.BootstrapTokenSecretPrefix, d.ID)
var lastErr error
for i := 0; i < tokenCreateRetries; i++ {
secret, err := client.Secrets(metav1.NamespaceSystem).Get(secretName, metav1.GetOptions{})
if err == nil {
// Secret with this ID already exists, update it:
secret.Data = encodeTokenSecretData(d, tokenDuration)
if _, err := client.Secrets(metav1.NamespaceSystem).Update(secret); err == nil {
return nil
} else {
lastErr = err
}
continue
}
// Secret does not already exist:
if apierrors.IsNotFound(err) {
secret = &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
},
Type: v1.SecretType(bootstrapapi.SecretTypeBootstrapToken),
Data: encodeTokenSecretData(d, tokenDuration),
}
if _, err := client.Secrets(metav1.NamespaceSystem).Create(secret); err == nil {
return nil
} else {
lastErr = err
}
continue
}
}
return fmt.Errorf(
"unable to create bootstrap token after %d attempts [%v]",
tokenCreateRetries,
lastErr,
)
}
// encodeTokenSecretData takes the token discovery object and an optional duration and returns the .Data for the Secret
func encodeTokenSecretData(d *kubeadmapi.TokenDiscovery, duration time.Duration) map[string][]byte {
data := map[string][]byte{
bootstrapapi.BootstrapTokenIDKey: []byte(d.ID),
bootstrapapi.BootstrapTokenSecretKey: []byte(d.Secret),
bootstrapapi.BootstrapTokenUsageSigningKey: []byte("true"),
}
if duration > 0 {
// Get the current time, add the specified duration, and format it accordingly
durationString := time.Now().Add(duration).Format(time.RFC3339)
data[bootstrapapi.BootstrapTokenExpirationKey] = []byte(durationString)
}
return data
}
/*
Copyright 2017 The Kubernetes Authors.
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 token
import (
"bytes"
"testing"
"time"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestEncodeTokenSecretData(t *testing.T) {
var tests = []struct {
token *kubeadmapi.TokenDiscovery
t time.Duration
}{
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}}, // should use default
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, t: time.Second}, // should use default
}
for _, rt := range tests {
actual := encodeTokenSecretData(rt.token, rt.t)
if !bytes.Equal(actual["token-id"], []byte(rt.token.ID)) {
t.Errorf(
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",
rt.token.ID,
actual["token-id"],
)
}
if !bytes.Equal(actual["token-secret"], []byte(rt.token.Secret)) {
t.Errorf(
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",
rt.token.Secret,
actual["token-secret"],
)
}
if rt.t > 0 {
if actual["expiration"] == nil {
t.Errorf(
"failed EncodeTokenSecretData, duration was not added to time",
)
}
}
}
}
/* /*
Copyright 2016 The Kubernetes Authors. Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and ...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package master package token
import ( import (
"bytes" "bytes"
...@@ -24,22 +24,17 @@ import ( ...@@ -24,22 +24,17 @@ import (
"k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/uuid"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
) )
const ( // CreateTokenAuthFile creates the CSV file that can be used for allowing users with tokens access to the API Server
// TODO: prefix with kubeadm prefix
KubeletBootstrapUser = "kubeadm-node-csr"
KubeletBootstrapGroup = "kubeadm:kubelet-bootstrap"
)
func CreateTokenAuthFile(bt string) error { func CreateTokenAuthFile(bt string) error {
tokenAuthFilePath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath, "tokens.csv") tokenAuthFilePath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath, kubeadmconstants.CSVTokenFileName)
if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.HostPKIPath, 0700); err != nil { if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.HostPKIPath, 0700); err != nil {
return fmt.Errorf("failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.HostPKIPath, err) return fmt.Errorf("failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.HostPKIPath, err)
} }
serialized := []byte(fmt.Sprintf("%s,%s,%s,%s\n", bt, KubeletBootstrapUser, uuid.NewUUID(), KubeletBootstrapGroup)) serialized := []byte(fmt.Sprintf("%s,%s,%s,%s\n", bt, kubeadmconstants.CSVTokenBootstrapUser, uuid.NewUUID(), kubeadmconstants.CSVTokenBootstrapGroup))
// DumpReaderToFile create a file with mode 0600 // DumpReaderToFile create a file with mode 0600
if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), tokenAuthFilePath); err != nil { if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), tokenAuthFilePath); err != nil {
return fmt.Errorf("failed to save token auth file (%q) [%v]", tokenAuthFilePath, err) return fmt.Errorf("failed to save token auth file (%q) [%v]", tokenAuthFilePath, err)
......
/*
Copyright 2017 The Kubernetes Authors.
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 token
...@@ -13,20 +13,10 @@ go_library( ...@@ -13,20 +13,10 @@ go_library(
srcs = [ srcs = [
"error.go", "error.go",
"template.go", "template.go",
"tokens.go",
"version.go", "version.go",
], ],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = ["//cmd/kubeadm/app/preflight:go_default_library"],
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//pkg/bootstrap/api:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/client-go/kubernetes",
"//vendor:k8s.io/client-go/pkg/api/v1",
],
) )
go_test( go_test(
...@@ -34,15 +24,11 @@ go_test( ...@@ -34,15 +24,11 @@ go_test(
srcs = [ srcs = [
"error_test.go", "error_test.go",
"template_test.go", "template_test.go",
"tokens_test.go",
"version_test.go", "version_test.go",
], ],
library = ":go_default_library", library = ":go_default_library",
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = ["//cmd/kubeadm/app/preflight:go_default_library"],
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
],
) )
filegroup( filegroup(
...@@ -54,6 +40,9 @@ filegroup( ...@@ -54,6 +40,9 @@ filegroup(
filegroup( filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [":package-srcs"], srcs = [
":package-srcs",
"//cmd/kubeadm/app/util/token:all-srcs",
],
tags = ["automanaged"], tags = ["automanaged"],
) )
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["tokens_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = ["tokens.go"],
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
/* /*
Copyright 2016 The Kubernetes Authors. Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -14,32 +14,21 @@ See the License for the specific language governing permissions and ...@@ -14,32 +14,21 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package util package token
import ( import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"regexp" "regexp"
"strconv"
"strings" "strings"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/pkg/api/v1"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
) )
const ( const (
TokenIDBytes = 3 TokenIDBytes = 3
TokenSecretBytes = 8 TokenSecretBytes = 8
BootstrapTokenSecretPrefix = "bootstrap-token-"
DefaultTokenDuration = time.Duration(8) * time.Hour
tokenCreateRetries = 5
) )
var ( var (
...@@ -84,7 +73,6 @@ func ParseTokenID(s string) error { ...@@ -84,7 +73,6 @@ func ParseTokenID(s string) error {
return fmt.Errorf("token ID [%q] was not of form [%q]", s, tokenIDRegexpString) return fmt.Errorf("token ID [%q] was not of form [%q]", s, tokenIDRegexpString)
} }
return nil return nil
} }
// ParseToken tries and parse a valid token from a string. // ParseToken tries and parse a valid token from a string.
...@@ -95,7 +83,6 @@ func ParseToken(s string) (string, string, error) { ...@@ -95,7 +83,6 @@ func ParseToken(s string) (string, string, error) {
return "", "", fmt.Errorf("token [%q] was not of form [%q]", s, tokenRegexpString) return "", "", fmt.Errorf("token [%q] was not of form [%q]", s, tokenRegexpString)
} }
return split[1], split[2], nil return split[1], split[2], nil
} }
// BearerToken returns a string representation of the passed token. // BearerToken returns a string representation of the passed token.
...@@ -111,86 +98,3 @@ func ValidateToken(d *kubeadmapi.TokenDiscovery) (bool, error) { ...@@ -111,86 +98,3 @@ func ValidateToken(d *kubeadmapi.TokenDiscovery) (bool, error) {
} }
return true, nil return true, nil
} }
func DiscoveryPort(d *kubeadmapi.TokenDiscovery) int32 {
if len(d.Addresses) == 0 {
return kubeadmapiext.DefaultDiscoveryBindPort
}
split := strings.Split(d.Addresses[0], ":")
if len(split) == 1 {
return kubeadmapiext.DefaultDiscoveryBindPort
}
if i, err := strconv.Atoi(split[1]); err == nil {
return int32(i)
}
return kubeadmapiext.DefaultDiscoveryBindPort
}
// UpdateOrCreateToken attempts to update a token with the given ID, or create if it does
// not already exist.
func UpdateOrCreateToken(client *clientset.Clientset, d *kubeadmapi.TokenDiscovery, tokenDuration time.Duration) error {
// Let's make sure
if valid, err := ValidateToken(d); !valid {
return err
}
secretName := fmt.Sprintf("%s%s", BootstrapTokenSecretPrefix, d.ID)
var lastErr error
for i := 0; i < tokenCreateRetries; i++ {
secret, err := client.Secrets(metav1.NamespaceSystem).Get(secretName, metav1.GetOptions{})
if err == nil {
// Secret with this ID already exists, update it:
secret.Data = encodeTokenSecretData(d, tokenDuration)
if _, err := client.Secrets(metav1.NamespaceSystem).Update(secret); err == nil {
return nil
} else {
lastErr = err
}
continue
}
// Secret does not already exist:
if apierrors.IsNotFound(err) {
secret = &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
},
// TODO(jbeda): convert kubeadm to client-go
// https://github.com/kubernetes/kubeadm/issues/52
Type: v1.SecretType(bootstrapapi.SecretTypeBootstrapToken),
Data: encodeTokenSecretData(d, tokenDuration),
}
if _, err := client.Secrets(metav1.NamespaceSystem).Create(secret); err == nil {
return nil
} else {
lastErr = err
}
continue
}
}
return fmt.Errorf(
"unable to create bootstrap token after %d attempts [%v]",
tokenCreateRetries,
lastErr,
)
}
func encodeTokenSecretData(d *kubeadmapi.TokenDiscovery, duration time.Duration) map[string][]byte {
var (
data = map[string][]byte{}
)
data["token-id"] = []byte(d.ID)
data["token-secret"] = []byte(d.Secret)
data["usage-bootstrap-signing"] = []byte("true")
if duration > 0 {
t := time.Now()
t = t.Add(duration)
data["expiration"] = []byte(t.Format(time.RFC3339))
}
return data
}
/* /*
Copyright 2016 The Kubernetes Authors. Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -14,12 +14,10 @@ See the License for the specific language governing permissions and ...@@ -14,12 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package util package token
import ( import (
"bytes"
"testing" "testing"
"time"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
) )
...@@ -151,28 +149,6 @@ func TestRandBytes(t *testing.T) { ...@@ -151,28 +149,6 @@ func TestRandBytes(t *testing.T) {
} }
} }
func TestDiscoveryPort(t *testing.T) {
var tests = []struct {
token *kubeadmapi.TokenDiscovery
expected int32
}{
{token: &kubeadmapi.TokenDiscovery{}, expected: 9898}, // should use default
{token: &kubeadmapi.TokenDiscovery{Addresses: []string{"foobar:1234"}}, expected: 1234},
{token: &kubeadmapi.TokenDiscovery{Addresses: []string{"doesnothaveport"}}, expected: 9898}, // should use default
{token: &kubeadmapi.TokenDiscovery{Addresses: []string{"foorbar:abcd"}}, expected: 9898}, // since abcd isn't an int, should use default
}
for _, rt := range tests {
actual := DiscoveryPort(rt.token)
if actual != rt.expected {
t.Errorf(
"failed DiscoveryPort:\n\texpected: %d\n\t actual: %d",
rt.expected,
actual,
)
}
}
}
func TestBearerToken(t *testing.T) { func TestBearerToken(t *testing.T) {
var tests = []struct { var tests = []struct {
token *kubeadmapi.TokenDiscovery token *kubeadmapi.TokenDiscovery
...@@ -191,37 +167,3 @@ func TestBearerToken(t *testing.T) { ...@@ -191,37 +167,3 @@ func TestBearerToken(t *testing.T) {
} }
} }
} }
func TestEncodeTokenSecretData(t *testing.T) {
var tests = []struct {
token *kubeadmapi.TokenDiscovery
t time.Duration
}{
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}}, // should use default
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, t: time.Second}, // should use default
}
for _, rt := range tests {
actual := encodeTokenSecretData(rt.token, rt.t)
if !bytes.Equal(actual["token-id"], []byte(rt.token.ID)) {
t.Errorf(
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",
rt.token.ID,
actual["token-id"],
)
}
if !bytes.Equal(actual["token-secret"], []byte(rt.token.Secret)) {
t.Errorf(
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",
rt.token.Secret,
actual["token-secret"],
)
}
if rt.t > 0 {
if actual["expiration"] == nil {
t.Errorf(
"failed EncodeTokenSecretData, duration was not added to time",
)
}
}
}
}
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