Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kubeadm: Support custom env in control plane component #118867

Merged
merged 2 commits into from
Jul 4, 2023

Conversation

chendave
Copy link
Member

@chendave chendave commented Jun 26, 2023

/kind feature

What type of PR is this?

What this PR does / why we need it:

There are some cases,  the custom environments such as `GOMAXPROCS` could be consumed by the control plane component.
This change supports the configuration of custom environments in control plane components with
the `ClusterConfiguration.Etcd.Local.ExtraEnvs`, `cfg.APIServer.ExtraEnvs`, `cfg.ControllerManager.ExtraEnvs`, `cfg.Scheduler.ExtraEnvs`.
Fixed some typos in the v1beta4/doc.go

Which issue(s) this PR fixes:

xref: kubernetes/kubeadm#2890

Special notes for your reviewer:

Does this PR introduce a user-facing change?

NONE

Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:


@k8s-ci-robot k8s-ci-robot added kind/feature Categorizes issue or PR as related to a new feature. size/M Denotes a PR that changes 30-99 lines, ignoring generated files. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. release-note-none Denotes a PR that doesn't merit a release note. and removed do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. labels Jun 26, 2023
@k8s-ci-robot k8s-ci-robot added area/kubeadm sig/cluster-lifecycle Categorizes an issue or PR as relevant to SIG Cluster Lifecycle. and removed do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. labels Jun 26, 2023
@chendave chendave mentioned this pull request Jun 26, 2023
26 tasks
@chendave
Copy link
Member Author

cc other members signed with the v1beta4.

/cc @neolit123 @ruquanzhao

I tested this against v1beta3, and it works, set the GOMAXPROCS in the config file and apiserver show me that env is set inside the container.

I0626 09:17:44.842730 1 server.go:167] "Golang settings" GOGC="" GOMAXPROCS="10" GOTRACEBACK=""

Copy link
Member

@neolit123 neolit123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @chendave
added some comments

cmd/kubeadm/app/apis/kubeadm/types.go Outdated Show resolved Hide resolved
cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go Outdated Show resolved Hide resolved
cmd/kubeadm/app/apis/kubeadm/types.go Outdated Show resolved Hide resolved
cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go Outdated Show resolved Hide resolved
cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go Outdated Show resolved Hide resolved
@@ -51,6 +53,9 @@ func TestGetStaticPodSpecs(t *testing.T) {
// Creates a Cluster Configuration
cfg := &kubeadmapi.ClusterConfiguration{
KubernetesVersion: "v1.9.0",
Scheduler: kubeadmapi.ControlPlaneComponent{ExtraEnvs: []v1.EnvVar{
{Name: "Foo", Value: "Bar"},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should have override / de-dup tests

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will be tested in cmd/kubeadm/app/util/env package.

@@ -28,8 +28,10 @@ import (
"strings"
"testing"

"github.com/google/go-cmp/cmp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already import reflect. maybe we can use it instead of the extra import of cmp

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think we should move from the reflect.DeepEqual to cmp.Diff,
here is the context,

I remember there is problem to compare two struct with embed struct when compare with reflect.DeepEqual

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reflect should be fine for slices, but i don't insist on not using cmp

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, I will switch to reflect in the next iteration.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@neolit123 have switched to reflect.

cmd/kubeadm/app/phases/etcd/local_test.go Show resolved Hide resolved
cmd/kubeadm/app/apis/kubeadm/types.go Outdated Show resolved Hide resolved
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Jun 27, 2023
@chendave chendave force-pushed the env_custom branch 2 times, most recently from 5df01d7 to 4b22b8b Compare June 27, 2023 08:54
@@ -41,3 +41,17 @@ func GetProxyEnvVars() []v1.EnvVar {
}
return envs
}

// MergedEnv merge the extraEnvs with proxyEnv, extraEnvs take precedence over the proxyEnv.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func should be called with a verb. MergeEnv

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be a generic, variadic argument function that takes a slice if env slices and makes the last slices override / take precedence.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, pls let me know if the current version address your comment.

@chendave chendave force-pushed the env_custom branch 2 times, most recently from 8bf3cb8 to 1f56b3d Compare June 28, 2023 07:30

if tc.env != nil {
envs := []v1.EnvVar{}
// Just compare the env that testcase set as the proxy env might be set an arbitrary value on the test node.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we must remove the "_proxy" env here, as the test node has the "no_proxy"env configured.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this sounds like it needs refactor to not have tests that depend on the host env. i am talking without checking, but the host env could be passed only on actual runs / outside of tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if am following here, correct me pls, how the host env is set seems not matter here as we just need to make sure the env set in the clusterCfg is parsed correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you've mentioned "test node", which i understood as the host where the unit test is run.

we must remove the "_proxy" env here, as the test node has the "no_proxy"env configured.

where does the no_proxy env come from?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@neolit123 neolit123 Jun 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems to me we should refactor our test / tested function here not to get:

[{no_proxy 127.0.0.1,localhost nil}

if i am not mistaken these come from the host?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this comes from the host. I will leave a TODO here so that we can revisit it later if we have a better way to tackle it.

Copy link
Member

@neolit123 neolit123 Jul 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we can fix it here as it's relatively simple.

  • make GetStaticPodSpecs() accept a []v1.EnvVar called proxyEnvs
  • if the slice is passed as nil populate it with GetProxyEnvVars() inside GetStaticPodSpecs() and use it in manifest construction
  • if the slice is non-nil use its value instead of GetProxyEnvVars()
  • then in this unit test just pass an empty slice

does it make sense to you?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks for the guidance!

Copy link
Member Author

@chendave chendave left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pacoxu I replied all your comments, pls let me know is that make sense to you, thanks!

// - TODO https://github.com/kubernetes/kubeadm/issues/2890
// - Support custom environment variables in control plane components under `ClusterConfiguration`.
// Use `APIServer.ExtraEnvs`, `ControllerManager.ExtraEnvs`, `Scheduler.ExtraEnvs`, `Etcd.Local.ExtraEnvs`.
Copy link
Member Author

@chendave chendave Jul 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

look like to me addons are not the resource maintained by kubeadm. @neolit123 @SataQiu anything to comment here?

// Get the required hostpath mounts
mounts := getHostPathVolumesForTheControlPlane(cfg)
if proxyEnvs == nil {
proxyEnvs = kubeadmutil.GetProxyEnvVars()
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was discussed here: #118867 (comment), I am fine with either of them but a little in favor of the current approach (we cannot guarantee extraEnv is always the last env, and actually it's a slice and we cannot inspect easily how many envs the user sets), the original approach is filter the proxy env out, @neolit123 suggest me to remove the dep over the host.

}{
{
name: "normal case without duplicated env",
proxyEnv: []v1.EnvVar{
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, this is just a testcase.

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: chendave, neolit123, pacoxu

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@@ -262,6 +267,10 @@ type LocalEtcd struct {
// command line except without leading dash(es).
ExtraArgs map[string]string

// ExtraEnvs is an extra set of environment variables to pass to the control plane component.
// +optional
ExtraEnvs []v1.EnvVar
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

@chendave chendave Jul 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pacoxu this is etcd, there is no such proxy env passed to etcd compared with ControlPlaneComponent, and we haven't overridden anything like what we do for ControlPlaneComponent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/hold

please add it everywhere. doesn't matter if we skip proxy.

actually why are we not adding proxy envs to etcd? isn't that a bug?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally etcd will not access outside, only peers should be accessed by etcd. And only local apiserver can access etcd.

For apiserver, it may access webhooks. Scheduler also can have webhook.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, maybe a peer would not be able to reach a peer because of proxy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no such a request for passing the proxy env so far, we can add it when there is a clear usecase, and this is not a API change, it will be easy to address then.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the comment for all the occurrences! @pacoxu @neolit123 looking for LGTM again.

Copy link
Member

@pacoxu pacoxu Jul 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like a corner case that is nice to support😄. Either is OK with me for this point.

@chendave
Copy link
Member Author

chendave commented Jul 3, 2023

@pacoxu anything more to add?

@pacoxu
Copy link
Member

pacoxu commented Jul 3, 2023

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 3, 2023
@k8s-ci-robot
Copy link
Contributor

LGTM label has been added.

Git tree hash: 8fb2cfb6b573ae8592b2bd2197ea3903e389444e

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 3, 2023
Signed-off-by: Dave Chen <dave.chen@arm.com>
…nent

Signed-off-by: Dave Chen <dave.chen@arm.com>
@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 4, 2023
@neolit123
Copy link
Member

/lgtm
/hold cancel

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 4, 2023
@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 4, 2023
@k8s-ci-robot
Copy link
Contributor

LGTM label has been added.

Git tree hash: 6c132e46b561d7c7442d9d2acb5e1b37749c0fc4

// ExtraEnvs is an extra set of environment variables to pass to the control plane component.
// Environment variables passed using ExtraEnvs will override any existing environment variables, or *_proxy environment variables that kubeadm adds by default.
// +optional
ExtraEnvs []corev1.EnvVar `json:"extraEnvs,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This caused the import of apis/core/v1 in generated.defaults below.

Should we avoid that or how to avoid the defaulting logic?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this not results to the import in generated_default.go?

https://github.com/kubernetes/kubernetes/pull/118867/files#r1321045615

@@ -23,6 +23,7 @@ package v1beta4

import (
runtime "k8s.io/apimachinery/pkg/runtime"
v1 "k8s.io/kubernetes/pkg/apis/core/v1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I tested, this is the key import that is related.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pacoxu as mentioned the kubernetes/pkg import is not allowed in kubeadm!

@chendave we must find out how to solve it. maybe it's a generator bug.
if we cannot solve it we can try avoiding the defaulting []...{}

if we are out of ideas, we must revert the PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must drop this import first.
In the past, we have made a lot of efforts to do this, and we cannot go back.
But I'm confused, why didn't the .import-restrictions prevent PRs from being merged?

Copy link
Member

@neolit123 neolit123 Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps the import-boss tool is not running or buggy.
it can be tested with a simple PR for cmd/kubeadm or other packages that have an .import-restrictions file.

EDIT: i do remember a recent contributor PR that failed with an import restriction problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it locally, and it seems that the generated code(zz_generated.defaults.go) won't be checked...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense. So generated code are skipped for that import restrictions.

Copy link
Member

@neolit123 neolit123 Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think code-gen here makes the decision poorly. it imports v1 "k8s.io/kubernetes/pkg/apis/core/v1" so that it can call the defaulter v1.SetDefaults_ObjectFieldSelector.

we have two options:

  1. play around with our types to make code-gen not import defaulters.
    seems to boil down to if a.ValueFrom != nil {
    an alias might trick the defaulter too type EnvVars []corev1.EnvVar...has to be tested.

  2. ask #sig-api-machinery if we can patch code-gen to not import the defaulters under certain conditions.
    maybe there is already +foo directive for it, like +optional.

Copy link
Member

@neolit123 neolit123 Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2. maybe there is already +foo directive for it, like +optional.

~/go/src/k8s.io/kubernetes$ git diff
diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go
index 314be0b30c7..38819c814f4 100644
--- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go
+++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go
@@ -74,6 +74,7 @@ func SetDefaults_InitConfiguration(obj *InitConfiguration) {
 }
 
 // SetDefaults_ClusterConfiguration assigns default values for the ClusterConfiguration
+// +k8s:defaulter-gen=covers
 func SetDefaults_ClusterConfiguration(obj *ClusterConfiguration) {
        if obj.KubernetesVersion == "" {
                obj.KubernetesVersion = DefaultKubernetesVersion

results in no import of k8s.io/kubernetes/pkg/api, after running ./hack/update-codegen.sh

// An existing defaulter method (SetDefaults_TYPE) can provide the
// comment tag:
//
// // +k8s:defaulter-gen=covers
//
// to indicate that the defaulter does not or should not call any nested
// defaulters.

https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/code-generator/cmd/defaulter-gen/main.go#L35-L41

Copy link
Member

@neolit123 neolit123 Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result diff:

diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.defaults.go
index b36b0f0f952..d2409b0de6a 100644
--- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.defaults.go
+++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.defaults.go
@@ -23,7 +23,6 @@ package v1beta4
 
 import (
        runtime "k8s.io/apimachinery/pkg/runtime"
-       v1 "k8s.io/kubernetes/pkg/apis/core/v1"
 )
 
 // RegisterDefaults adds defaulters functions to the given scheme.
@@ -39,41 +38,6 @@ func RegisterDefaults(scheme *runtime.Scheme) error {
 
 func SetObjectDefaults_ClusterConfiguration(in *ClusterConfiguration) {
        SetDefaults_ClusterConfiguration(in)
-       if in.Etcd.Local != nil {
-               for i := range in.Etcd.Local.ExtraEnvs {
-                       a := &in.Etcd.Local.ExtraEnvs[i]
-                       if a.ValueFrom != nil {
-                               if a.ValueFrom.FieldRef != nil {
-                                       v1.SetDefaults_ObjectFieldSelector(a.ValueFrom.FieldRef)
-                               }
-                       }
-               }
-       }
-       SetDefaults_APIServer(&in.APIServer)
-       for i := range in.APIServer.ControlPlaneComponent.ExtraEnvs {
-               a := &in.APIServer.ControlPlaneComponent.ExtraEnvs[i]
-               if a.ValueFrom != nil {
-                       if a.ValueFrom.FieldRef != nil {
-                               v1.SetDefaults_ObjectFieldSelector(a.ValueFrom.FieldRef)
-                       }
-               }
-       }
-       for i := range in.ControllerManager.ExtraEnvs {
-               a := &in.ControllerManager.ExtraEnvs[i]
-               if a.ValueFrom != nil {
-                       if a.ValueFrom.FieldRef != nil {
-                               v1.SetDefaults_ObjectFieldSelector(a.ValueFrom.FieldRef)
-                       }
-               }
-       }

questions:

  • would that be a problem for us in the future? probably no, because if we want a defaulter for some field in ClusterConfiguration we can explicitly call it in SetDefaults_ClusterConfiguration()
  • can this missing defaulting for a.ValueFrom.FieldRef be a problem? i think no. it just sets that field selector to "v1", but technically the kube-apiserver must also do this defaulting when the mirror Pod (of the static pod) is created, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, I like this approach, it's more neat to look!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. area/kubeadm cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. priority/important-soon Must be staffed and worked on either currently, or very soon, ideally in time for the next release. release-note-none Denotes a PR that doesn't merit a release note. sig/cluster-lifecycle Categorizes an issue or PR as relevant to SIG Cluster Lifecycle. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. triage/accepted Indicates an issue or PR is ready to be actively worked on.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants