Skip to content

Commit fceb39e

Browse files
committed
kubeadm: ensure proper parsing of SSR username
- Split the code that tries to get node name from SSR into a new function getNodeNameFromSSR(). Unit test the function. - Fix error that the "system:nodes:" prefix was not trimmed. - Fix mislearding errors around FetchInitConfigurationFromCluster. This function performs multiple actions, and the "get node" action can also be of type apierrors.NotFound(). This creates confusion in the returned error in enforceRequirement during upgrade. Fix this problem.
1 parent c4eea34 commit fceb39e

File tree

3 files changed

+96
-15
lines changed

3 files changed

+96
-15
lines changed

cmd/kubeadm/app/cmd/upgrade/common.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import (
2626
"github.com/pkg/errors"
2727
"github.com/spf13/pflag"
2828

29-
apierrors "k8s.io/apimachinery/pkg/api/errors"
30-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3129
"k8s.io/apimachinery/pkg/util/sets"
3230
fakediscovery "k8s.io/client-go/discovery/fake"
3331
clientset "k8s.io/client-go/kubernetes"
@@ -96,11 +94,6 @@ func enforceRequirements(flagSet *pflag.FlagSet, flags *applyPlanFlags, args []s
9694

9795
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, "upgrade/config", false, false)
9896
if err != nil {
99-
if apierrors.IsNotFound(err) {
100-
_, _ = printer.Printf("[upgrade/config] In order to upgrade, a ConfigMap called %q in the %q namespace must exist.\n", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
101-
_, _ = printer.Printf("[upgrade/config] Use 'kubeadm init phase upload-config --config your-config.yaml' to re-upload it.\n")
102-
err = errors.Errorf("the ConfigMap %q in the %q namespace was not found", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
103-
}
10497
return nil, nil, nil, nil, errors.Wrap(err, "[upgrade/init config] FATAL")
10598
}
10699

cmd/kubeadm/app/util/config/cluster.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ func FetchInitConfigurationFromCluster(client clientset.Interface, printer outpu
5353
if printer == nil {
5454
printer = &output.TextPrinter{}
5555
}
56-
printer.Printf("[%s] Reading configuration from the cluster...\n", logPrefix)
57-
printer.Printf("[%s] FYI: You can look at this config file with 'kubectl -n %s get cm %s -o yaml'\n", logPrefix, metav1.NamespaceSystem, constants.KubeadmConfigConfigMap)
56+
_, _ = printer.Printf("[%s] Reading configuration from the %q ConfigMap in namespace %q...\n",
57+
logPrefix, constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
58+
_, _ = printer.Printf("[%s] Use 'kubeadm init phase upload-config --config your-config.yaml' to re-upload it.\n", logPrefix)
5859

5960
// Fetch the actual config from cluster
6061
cfg, err := getInitConfigurationFromCluster(constants.KubernetesDir, client, newControlPlane, skipComponentConfigs)
@@ -139,11 +140,9 @@ func GetNodeName(kubeconfigFile string) (string, error) {
139140
if kubeconfigFile != "" {
140141
client, err := kubeconfig.ClientSetFromFile(kubeconfigFile)
141142
if err == nil {
142-
ssr, err := client.AuthenticationV1().SelfSubjectReviews().
143-
Create(context.Background(), &authv1.SelfSubjectReview{}, metav1.CreateOptions{})
144-
145-
if err == nil && ssr.Status.UserInfo.Username != "" {
146-
return ssr.Status.UserInfo.Username, nil
143+
nodeName, err = getNodeNameFromSSR(client)
144+
if err == nil {
145+
return nodeName, nil
147146
}
148147
}
149148
nodeName, err = getNodeNameFromKubeletConfig(kubeconfigFile)
@@ -167,7 +166,7 @@ func GetNodeRegistration(kubeconfigFile string, client clientset.Interface, node
167166
}
168167

169168
// gets the corresponding node and retrieves attributes stored there.
170-
node, err := client.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
169+
node, err := client.CoreV1().Nodes().Get(context.Background(), nodeName, metav1.GetOptions{})
171170
if err != nil {
172171
return errors.Wrap(err, "failed to get corresponding node")
173172
}
@@ -231,6 +230,22 @@ func getNodeNameFromKubeletConfig(fileName string) (string, error) {
231230
return strings.TrimPrefix(cert.Subject.CommonName, constants.NodesUserPrefix), nil
232231
}
233232

233+
// getNodeNameFromSSR reads the node name from the SelfSubjectReview for a given client.
234+
// If the kubelet.conf is passed as fileName it can be used to retrieve the node name.
235+
func getNodeNameFromSSR(client clientset.Interface) (string, error) {
236+
ssr, err := client.AuthenticationV1().SelfSubjectReviews().
237+
Create(context.Background(), &authv1.SelfSubjectReview{}, metav1.CreateOptions{})
238+
if err != nil {
239+
return "", err
240+
}
241+
user := ssr.Status.UserInfo.Username
242+
if !strings.HasPrefix(user, constants.NodesUserPrefix) {
243+
return "", errors.Errorf("%q is not a node client, must have %q prefix in the name",
244+
user, constants.NodesUserPrefix)
245+
}
246+
return strings.TrimPrefix(user, constants.NodesUserPrefix), nil
247+
}
248+
234249
func getAPIEndpoint(client clientset.Interface, nodeName string, apiEndpoint *kubeadmapi.APIEndpoint) error {
235250
return getAPIEndpointWithRetry(client, nodeName, apiEndpoint,
236251
constants.KubernetesAPICallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration)

cmd/kubeadm/app/util/config/cluster_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929

3030
"github.com/pkg/errors"
3131

32+
authv1 "k8s.io/api/authentication/v1"
3233
v1 "k8s.io/api/core/v1"
3334
apierrors "k8s.io/apimachinery/pkg/api/errors"
3435
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -860,3 +861,75 @@ func TestGetRawAPIEndpointFromPodAnnotationWithoutRetry(t *testing.T) {
860861
})
861862
}
862863
}
864+
865+
func TestGetNodeNameFromSSR(t *testing.T) {
866+
var tests = []struct {
867+
name string
868+
clientSetup func(*clientsetfake.Clientset)
869+
expectedNodeName string
870+
expectedError bool
871+
}{
872+
{
873+
name: "valid node name",
874+
clientSetup: func(clientset *clientsetfake.Clientset) {
875+
clientset.PrependReactor("create", "selfsubjectreviews",
876+
func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
877+
obj := &authv1.SelfSubjectReview{
878+
Status: authv1.SelfSubjectReviewStatus{
879+
UserInfo: authv1.UserInfo{
880+
Username: kubeadmconstants.NodesUserPrefix + "foo",
881+
},
882+
},
883+
}
884+
return true, obj, nil
885+
})
886+
},
887+
expectedNodeName: "foo",
888+
expectedError: false,
889+
},
890+
{
891+
name: "SSR created but client is not a node",
892+
clientSetup: func(clientset *clientsetfake.Clientset) {
893+
clientset.PrependReactor("create", "selfsubjectreviews",
894+
func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
895+
obj := &authv1.SelfSubjectReview{
896+
Status: authv1.SelfSubjectReviewStatus{
897+
UserInfo: authv1.UserInfo{
898+
Username: "foo",
899+
},
900+
},
901+
}
902+
return true, obj, nil
903+
})
904+
},
905+
expectedNodeName: "",
906+
expectedError: true,
907+
},
908+
{
909+
name: "error creating SSR",
910+
clientSetup: func(clientset *clientsetfake.Clientset) {
911+
clientset.PrependReactor("create", "selfsubjectreviews",
912+
func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
913+
return true, nil, errors.New("")
914+
})
915+
},
916+
expectedNodeName: "",
917+
expectedError: true,
918+
},
919+
}
920+
for _, rt := range tests {
921+
t.Run(rt.name, func(t *testing.T) {
922+
client := clientsetfake.NewSimpleClientset()
923+
rt.clientSetup(client)
924+
925+
nodeName, err := getNodeNameFromSSR(client)
926+
927+
if (err != nil) != rt.expectedError {
928+
t.Fatalf("expected error: %+v, got: %+v", rt.expectedError, err)
929+
}
930+
if rt.expectedNodeName != nodeName {
931+
t.Fatalf("expected nodeName: %s, got: %s", rt.expectedNodeName, nodeName)
932+
}
933+
})
934+
}
935+
}

0 commit comments

Comments
 (0)