Refactor nodeup script to avoid action-at-a-distance

This commit is contained in:
justinsb 2021-12-17 16:55:39 -05:00
parent 648858a78a
commit e2c28b062b
7 changed files with 146 additions and 88 deletions

1
pkg/model/BUILD.bazel generated
View File

@ -11,7 +11,6 @@ go_library(
"master_volumes.go",
"names.go",
"pki.go",
"template_resource.go",
],
importpath = "k8s.io/kops/pkg/model",
visibility = ["//visibility:public"],

View File

@ -18,7 +18,6 @@ package model
import (
"bytes"
"compress/gzip"
"crypto/sha256"
"encoding/base64"
"fmt"
@ -26,7 +25,6 @@ import (
"sort"
"strconv"
"strings"
"text/template"
"k8s.io/klog/v2"
"k8s.io/kops/pkg/apis/kops/model"
@ -227,11 +225,7 @@ func (b *BootstrapScriptBuilder) ResourceNodeUp(c *fi.ModelBuilderContext, ig *k
// Bastions can have AdditionalUserData, but if there isn't any skip this part
if len(ig.Spec.AdditionalUserData) == 0 {
templateResource, err := NewTemplateResource("nodeup", "", nil, nil)
if err != nil {
return nil, err
}
return templateResource, nil
return fi.NewStringResource(""), nil
}
}
@ -295,36 +289,12 @@ func (b *BootstrapScript) Run(c *fi.Context) error {
return err
}
functions := template.FuncMap{
"NodeUpSourceAmd64": func() string {
if b.builder.NodeUpAssets[architectures.ArchitectureAmd64] != nil {
return strings.Join(b.builder.NodeUpAssets[architectures.ArchitectureAmd64].Locations, ",")
}
return ""
},
"NodeUpSourceHashAmd64": func() string {
if b.builder.NodeUpAssets[architectures.ArchitectureAmd64] != nil {
return b.builder.NodeUpAssets[architectures.ArchitectureAmd64].Hash.Hex()
}
return ""
},
"NodeUpSourceArm64": func() string {
if b.builder.NodeUpAssets[architectures.ArchitectureArm64] != nil {
return strings.Join(b.builder.NodeUpAssets[architectures.ArchitectureArm64].Locations, ",")
}
return ""
},
"NodeUpSourceHashArm64": func() string {
if b.builder.NodeUpAssets[architectures.ArchitectureArm64] != nil {
return b.builder.NodeUpAssets[architectures.ArchitectureArm64].Hash.Hex()
}
return ""
},
"KubeEnv": func() string {
return config
},
var nodeupScript resources.NodeUpScript
nodeupScript.NodeUpAssets = b.builder.NodeUpAssets
nodeupScript.KubeEnv = config
"EnvironmentVariables": func() (string, error) {
{
nodeupScript.EnvironmentVariables = func() (string, error) {
env, err := b.buildEnvironmentVariables(c.Cluster)
if err != nil {
return "", err
@ -342,13 +312,13 @@ func (b *BootstrapScript) Run(c *fi.Context) error {
b.WriteString(fmt.Sprintf("export %s=%s\n", k, env[k]))
}
return b.String(), nil
},
}
"ProxyEnv": func() string {
nodeupScript.ProxyEnv = func() (string, error) {
return b.createProxyEnv(c.Cluster.Spec.EgressProxy)
},
}
"ClusterSpec": func() (string, error) {
nodeupScript.ClusterSpec = func() (string, error) {
cs := c.Cluster.Spec
spec := make(map[string]interface{})
@ -399,24 +369,16 @@ func (b *BootstrapScript) Run(c *fi.Context) error {
return "", fmt.Errorf("error converting cluster spec to yaml for inclusion within bootstrap script: %v", err)
}
return string(content), nil
},
"CompressUserData": func() *bool {
return b.ig.Spec.CompressUserData
},
"GzipBase64": func(data string) (string, error) {
return gzipBase64(data)
},
"SetSysctls": func() string {
// By setting some sysctls early, we avoid broken configurations that prevent nodeup download.
// See https://github.com/kubernetes/kops/issues/10206 for details.
return setSysctls()
},
}
}
nodeupScriptResource, err := NewTemplateResource("nodeup", resources.NodeUpTemplate, functions, nil)
nodeupScript.CompressUserData = fi.BoolValue(b.ig.Spec.CompressUserData)
// By setting some sysctls early, we avoid broken configurations that prevent nodeup download.
// See https://github.com/kubernetes/kops/issues/10206 for details.
nodeupScript.SetSysctls = setSysctls()
nodeupScriptResource, err := nodeupScript.Build()
if err != nil {
return err
}
@ -437,7 +399,7 @@ func (b *BootstrapScript) Run(c *fi.Context) error {
return nil
}
func (b *BootstrapScript) createProxyEnv(ps *kops.EgressProxySpec) string {
func (b *BootstrapScript) createProxyEnv(ps *kops.EgressProxySpec) (string, error) {
var buffer bytes.Buffer
if ps != nil && ps.HTTPProxy.Host != "" {
@ -484,27 +446,7 @@ func (b *BootstrapScript) createProxyEnv(ps *kops.EgressProxySpec) string {
buffer.WriteString("systemctl daemon-reload\n")
buffer.WriteString("systemctl daemon-reexec\n")
}
return buffer.String()
}
func gzipBase64(data string) (string, error) {
var b bytes.Buffer
gz := gzip.NewWriter(&b)
_, err := gz.Write([]byte(data))
if err != nil {
return "", err
}
if err = gz.Flush(); err != nil {
return "", err
}
if err = gz.Close(); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(b.Bytes()), nil
return buffer.String(), nil
}
func setSysctls() string {

View File

@ -44,8 +44,10 @@ func Test_ProxyFunc(t *testing.T) {
},
}
script := b.createProxyEnv(ps)
script, err := b.createProxyEnv(ps)
if err != nil {
t.Fatalf("createProxyEnv failed: %v", err)
}
if script == "" {
t.Fatalf("script cannot be empty")
}
@ -56,7 +58,11 @@ func Test_ProxyFunc(t *testing.T) {
ps.ProxyExcludes = "www.google.com,www.kubernetes.io"
script = b.createProxyEnv(ps)
script, err = b.createProxyEnv(ps)
if err != nil {
t.Fatalf("createProxyEnv failed: %v", err)
}
if !strings.Contains(script, "no_proxy="+ps.ProxyExcludes) {
t.Fatalf("script not setting no_proxy properly")
}

View File

@ -2,10 +2,18 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["nodeup.go"],
srcs = [
"nodeup.go",
"template_resource.go",
],
importpath = "k8s.io/kops/pkg/model/resources",
visibility = ["//visibility:public"],
deps = ["//pkg/apis/kops:go_default_library"],
deps = [
"//pkg/apis/kops:go_default_library",
"//upup/pkg/fi:go_default_library",
"//util/pkg/architectures:go_default_library",
"//util/pkg/mirrors:go_default_library",
],
)
go_test(

View File

@ -19,14 +19,21 @@ package resources
import (
"bufio"
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"
"mime/multipart"
"net/textproto"
"strings"
"text/template"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/util/pkg/architectures"
"k8s.io/kops/util/pkg/mirrors"
)
var NodeUpTemplate = `#!/bin/bash
var nodeUpTemplate = `#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
@ -165,6 +172,102 @@ download-release
echo "== nodeup node config done =="
`
// NodeUpScript is responsible for creating the nodeup script
type NodeUpScript struct {
NodeUpAssets map[architectures.Architecture]*mirrors.MirroredAsset
KubeEnv string
CompressUserData bool
SetSysctls string
ProxyEnv func() (string, error)
EnvironmentVariables func() (string, error)
ClusterSpec func() (string, error)
}
func funcEmptyString() (string, error) {
return "", nil
}
func (b *NodeUpScript) Build() (fi.Resource, error) {
if b.ProxyEnv == nil {
b.ProxyEnv = funcEmptyString
}
if b.EnvironmentVariables == nil {
b.EnvironmentVariables = funcEmptyString
}
if b.ClusterSpec == nil {
b.ClusterSpec = funcEmptyString
}
functions := template.FuncMap{
"NodeUpSourceAmd64": func() string {
if b.NodeUpAssets[architectures.ArchitectureAmd64] != nil {
return strings.Join(b.NodeUpAssets[architectures.ArchitectureAmd64].Locations, ",")
}
return ""
},
"NodeUpSourceHashAmd64": func() string {
if b.NodeUpAssets[architectures.ArchitectureAmd64] != nil {
return b.NodeUpAssets[architectures.ArchitectureAmd64].Hash.Hex()
}
return ""
},
"NodeUpSourceArm64": func() string {
if b.NodeUpAssets[architectures.ArchitectureArm64] != nil {
return strings.Join(b.NodeUpAssets[architectures.ArchitectureArm64].Locations, ",")
}
return ""
},
"NodeUpSourceHashArm64": func() string {
if b.NodeUpAssets[architectures.ArchitectureArm64] != nil {
return b.NodeUpAssets[architectures.ArchitectureArm64].Hash.Hex()
}
return ""
},
"KubeEnv": func() string {
return b.KubeEnv
},
"GzipBase64": func(data string) (string, error) {
return gzipBase64(data)
},
"CompressUserData": func() bool {
return b.CompressUserData
},
"SetSysctls": func() string {
return b.SetSysctls
},
"ProxyEnv": b.ProxyEnv,
"EnvironmentVariables": b.EnvironmentVariables,
"ClusterSpec": b.ClusterSpec,
}
return newTemplateResource("nodeup", nodeUpTemplate, functions, nil)
}
func gzipBase64(data string) (string, error) {
var b bytes.Buffer
gz := gzip.NewWriter(&b)
_, err := gz.Write([]byte(data))
if err != nil {
return "", err
}
if err = gz.Flush(); err != nil {
return "", err
}
if err = gz.Close(); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(b.Bytes()), nil
}
// AWSMultipartMIME returns a MIME Multi Part Archive containing the nodeup (bootstrap) script
// and any additional User Data passed to using AdditionalUserData in the IG Spec
func AWSMultipartMIME(bootScript string, ig *kops.InstanceGroup) (string, error) {

View File

@ -22,7 +22,7 @@ import (
)
func Test_NodeUpTabs(t *testing.T) {
for i, line := range strings.Split(NodeUpTemplate, "\n") {
for i, line := range strings.Split(nodeUpTemplate, "\n") {
if strings.Contains(line, "\t") {
t.Errorf("NodeUpTemplate contains unexpected character %q on line %d: %q", "\t", i, line)
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package model
package resources
import (
"bytes"
@ -35,7 +35,7 @@ type templateResource struct {
var _ fi.Resource = &templateResource{}
func NewTemplateResource(key string, definition string, functions template.FuncMap, context interface{}) (*templateResource, error) {
func newTemplateResource(key string, definition string, functions template.FuncMap, context interface{}) (*templateResource, error) {
r := &templateResource{
key: key,
definition: definition,