Repository: grafana/tanka
Pull request #579 (2021-07-02 07:52:39)

chore: s/k8s-alpha/k8s-libsonnet
ref: https://github.com/jsonnet-libs/k8s/pull/39

This library was renamed upstream.

malcolmholmes: Does k8s-alpha still exist? Is this a clone and deprecate or a move? If the former, then looks great.
Duologic: > Does k8s-alpha still exist? Is this a clone and deprecate or a move? If the former, then looks great.

It still exists, a PR is made to show a deprecation notice: https://github.com/jsonnet-libs/k8s-alpha/pull/10

Patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..008074e2
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+    "go.formatTool": "gofmt"
+}
\ No newline at end of file
diff --git a/cmd/tk/init.go b/cmd/tk/init.go
index 09b1b854..576b43a3 100644
--- a/cmd/tk/init.go
+++ b/cmd/tk/init.go
@@ -25,7 +25,7 @@ func initCmd() *cli.Command {
 	}
 
 	force := cmd.Flags().BoolP("force", "f", false, "ignore the working directory not being empty")
-	installK8s := cmd.Flags().String("k8s", defaultK8sVersion, "choose the version of k8s-alpha, set to false to skip")
+	installK8s := cmd.Flags().String("k8s", defaultK8sVersion, "choose the version of k8s-libsonnet, set to false to skip")
 	inline := cmd.Flags().BoolP("inline", "i", false, "create an inline environment")
 
 	cmd.Run = func(cmd *cli.Command, args []string) error {
@@ -99,11 +99,11 @@ func installK8sLib(version string) error {
 	}
 
 	var initialPackages = []string{
-		"github.com/jsonnet-libs/k8s-alpha/" + version,
+		"github.com/jsonnet-libs/k8s-libsonnet/" + version + "@main",
 		"github.com/grafana/jsonnet-libs/ksonnet-util",
 	}
 
-	if err := writeNewFile("lib/k.libsonnet", "import 'github.com/jsonnet-libs/k8s-alpha/"+version+"/main.libsonnet'\n"); err != nil {
+	if err := writeNewFile("lib/k.libsonnet", "import 'github.com/jsonnet-libs/k8s-libsonnet/"+version+"/main.libsonnet'\n"); err != nil {
 		return err
 	}
 
diff --git a/docs/docs/directory-structure.md b/docs/docs/directory-structure.md
index 842993e5..9294933a 100644
--- a/docs/docs/directory-structure.md
+++ b/docs/docs/directory-structure.md
@@ -17,7 +17,7 @@ Tanka uses the following directories and special files:
 ├── jsonnetfile.json # direct dependencies
 ├── jsonnetfile.lock.json # all dependencies with exact versions
 ├── lib # libraries for this project only
-│   └── k.libsonnet # alias file for vendor/github.com/jsonnet-libs/k8s-alpha/1.21/main.libsonnet
+│   └── k.libsonnet # alias file for vendor/github.com/jsonnet-libs/k8s-libsonnet/1.21/main.libsonnet
 └── vendor # external libraries installed using jb
     ├── github.com
     │   ├── grafana
@@ -26,11 +26,11 @@ Tanka uses the following directories and special files:
     │   │           ├── ...
     │   │           └── kausal.libsonnet
     │   └── jsonnet-libs
-    │       └── k8s-alpha
+    │       └── k8s-libsonnet
     │           └── 1.21 # kubernetes library
     │               ├── ...
     │               └── main.libsonnet
-    ├── 1.21 -> github.com/jsonnet-libs/k8s-alpha/1.21
+    ├── 1.21 -> github.com/jsonnet-libs/k8s-libsonnet/1.21
     └── ksonnet-util -> github.com/grafana/jsonnet-libs/ksonnet-util
 ```
 
diff --git a/docs/docs/helm.mdx b/docs/docs/helm.mdx
index 1301db7a..5a4d79f3 100644
--- a/docs/docs/helm.mdx
+++ b/docs/docs/helm.mdx
@@ -83,6 +83,8 @@ The following options control how the command is invoked:
     includeCrds: true,
     // Equivalent to: --api-versions v1 --api-versions apps/v1
     apiVersions: ['v1', 'apps/v1']
+    // Equivalent to: --kube-version v1.20.0
+    kubeVersion: 'v1.20.0'
     // Equivalent to: --no-hooks
     noHooks: true,
 }
diff --git a/docs/docs/known-issues.md b/docs/docs/known-issues.md
index 71c71a48..5523e2ac 100644
--- a/docs/docs/known-issues.md
+++ b/docs/docs/known-issues.md
@@ -18,18 +18,18 @@ requires your code to be inside of one of the
 
 ### Evaluating jsonnet: RUNTIME ERROR: couldn't open import "k.libsonnet": no match locally or in the Jsonnet library paths
 
-This error can occur when the `k8s-alpha` kubernetes libraries are missing in the
-import paths. While `k8s-alpha` used to magically include them, Tanka follows a
+This error can occur when the `k8s-libsonnet` kubernetes libraries are missing in the
+import paths. While `k8s-libsonnet` used to magically include them, Tanka follows a
 more explicit approach and requires you to install them using `jb`:
 
 ```bash
-$ jb install github.com/jsonnet-libs/k8s-alpha/1.21
-$ echo "import 'github.com/jsonnet-libs/k8s-alpha/1.21/main.libsonnet'" > lib/k.libsonnet
+$ jb install github.com/jsonnet-libs/k8s-libsonnet/1.21@main
+$ echo "import 'github.com/jsonnet-libs/k8s-libsonnet/1.21/main.libsonnet'" > lib/k.libsonnet
 ```
 
 This does 2 things:
 
-1) It installs the `k8s-alpha` library (in `vendor/github.com/jsonnet-libs/k8s-alpha/1.21/`).
+1) It installs the `k8s-libsonnet` library (in `vendor/github.com/jsonnet-libs/k8s-libsonnet/1.21/`).
 You can replace the `1.21` matching the Kubernetes version you want to run against.
 
 2) It makes an alias for libraries importing `k.libsonnet` directly. See
diff --git a/docs/docs/tutorial/k-lib.mdx b/docs/docs/tutorial/k-lib.mdx
index 2a45014f..2f65e01b 100644
--- a/docs/docs/tutorial/k-lib.mdx
+++ b/docs/docs/tutorial/k-lib.mdx
@@ -15,37 +15,37 @@ Writing and maintaining such a library could be a full-time job on it's own.
 Luckily, it is possible to generate such a library from the Kubernetes OpenAPI
 specification! Even better, it has already been done for you.
 
-## k8s-alpha
+## k8s-libsonnet
 
-The library is called `k8s-alpha` (replacing the discontinued `ksonnet-lib`),
-currently available at https://github.com/jsonnet-libs/k8s-alpha.
+The library is called `k8s-libsonnet` (replacing the discontinued `ksonnet-lib`),
+currently available at https://github.com/jsonnet-libs/k8s-libsonnet.
 
 > **Note**: The `ksonnet` project has been abandoned, the library is not maintained
 > anymore. However, the community backed by Grafana Labs has picked up on this with
-> the `k8s-alpha` library.
+> the `k8s-libsonnet` library.
 
-As `k8s-alpha` has broken compatibility in a few places with `ksonnet-lib` (for good
+As `k8s-libsonnet` has broken compatibility in a few places with `ksonnet-lib` (for good
 reason), we have instrumented the widely used `ksonnet-util` library with a
 compatibility layer to improve the developer and user experience:
 https://github.com/grafana/jsonnet-libs/tree/master/ksonnet-util
 
 If you do not have any strong reasons against it, just adopt the wrapper as
 well, it will ease your work. Many of the original `ksonnet-util` enhancements
-have already made their way into `k8s-alpha`.
+have already made their way into `k8s-libsonnet`.
 
-The docs for `k8s-alpha` library can be found here:
-https://jsonnet-libs.github.io/k8s-alpha/
+The docs for `k8s-libsonnet` library can be found here:
+https://jsonnet-libs.github.io/k8s-libsonnet/
 
 ## Installation
 
-Like every other external library, `k8s-alpha` can be installed using
+Like every other external library, `k8s-libsonnet` can be installed using
 `jsonnet-bundler`.
 However, Tanka already **did this for you** during [project
 creation (`tk init`)](/tutorial/jsonnet#creating-a-new-project):
 
 ```bash
 $ tk init
-  └─ jb install github.com/jsonnet-libs/k8s-alpha/1.21 github.com/grafana/jsonnet-libs/ksonnet-util
+  └─ jb install github.com/jsonnet-libs/k8s-libsonnet/1.21@main github.com/grafana/jsonnet-libs/ksonnet-util
 
 ```
 
@@ -60,11 +60,11 @@ vendor
 │   │           ├── ...
 │   │           └── kausal.libsonnet # Grafana's wrapper
 │   └── jsonnet-libs
-│       └── k8s-alpha
+│       └── k8s-libsonnet
 │           └── 1.21
 │               ├── ...
-│               └── main.libsonnet   # k8s-alpha entrypoint
-├── 1.21 -> github.com/jsonnet-libs/k8s-alpha/1.21
+│               └── main.libsonnet   # k8s-libsonnet entrypoint
+├── 1.21 -> github.com/jsonnet-libs/k8s-libsonnet/1.21
 └── ksonnet-util -> github.com/grafana/jsonnet-libs/ksonnet-util
 ```
 
@@ -74,7 +74,7 @@ vendor
 
 #### Aliasing
 Because of how `jb` works, the library can be imported as
-`github.com/jsonnet-libs/k8s-alpha/1.21/main.libsonnet`.  Most external
+`github.com/jsonnet-libs/k8s-libsonnet/1.21/main.libsonnet`.  Most external
 libraries (including our wrapper) expect it as a simple `k.libsonnet` (without
 the package prefix).
 
@@ -84,7 +84,7 @@ alternative name as well.
 
 > **More information**:
 > This works, because `import` behaves like copy-pasting. So the contents of
-> `k8s-alpha/1.21` are "copied" into our new file, making them behave exactly the
+> `k8s-libsonnet/1.21` are "copied" into our new file, making them behave exactly the
 > same.
 
 ## Using it
diff --git a/examples/prom-grafana/jsonnetfile.json b/examples/prom-grafana/jsonnetfile.json
index bb91cf73..0e34430d 100644
--- a/examples/prom-grafana/jsonnetfile.json
+++ b/examples/prom-grafana/jsonnetfile.json
@@ -13,11 +13,11 @@
     {
       "source": {
         "git": {
-          "remote": "https://github.com/jsonnet-libs/k8s-alpha.git",
+          "remote": "https://github.com/jsonnet-libs/k8s-libsonnet.git",
           "subdir": "1.21"
         }
       },
-      "version": "master"
+      "version": "main"
     }
   ],
   "legacyImports": true
diff --git a/examples/prom-grafana/jsonnetfile.lock.json b/examples/prom-grafana/jsonnetfile.lock.json
index e34896d4..3f6911a9 100644
--- a/examples/prom-grafana/jsonnetfile.lock.json
+++ b/examples/prom-grafana/jsonnetfile.lock.json
@@ -14,12 +14,12 @@
     {
       "source": {
         "git": {
-          "remote": "https://github.com/jsonnet-libs/k8s-alpha.git",
+          "remote": "https://github.com/jsonnet-libs/k8s-libsonnet.git",
           "subdir": "1.21"
         }
       },
-      "version": "b3200f1ead8ebb74932673a41748194950564686",
-      "sum": "QVUm2JrNnhjorXlfsNBDV0uvPgSWUolMYG9UzWLo1qQ="
+      "version": "e02dc383505f699ba525861303d167387912278e",
+      "sum": "nwLFNxWjkftavkDSIFcme+t3KowULjBJn/lgcghru+o="
     }
   ],
   "legacyImports": false
diff --git a/examples/prom-grafana/lib/k.libsonnet b/examples/prom-grafana/lib/k.libsonnet
index 4102b723..da3161d5 100644
--- a/examples/prom-grafana/lib/k.libsonnet
+++ b/examples/prom-grafana/lib/k.libsonnet
@@ -1,1 +1,1 @@
-import 'github.com/jsonnet-libs/k8s-alpha/1.21/main.libsonnet'
+import 'github.com/jsonnet-libs/k8s-libsonnet/1.21/main.libsonnet'
diff --git a/pkg/helm/template.go b/pkg/helm/template.go
index 3e56b52c..bf64c493 100644
--- a/pkg/helm/template.go
+++ b/pkg/helm/template.go
@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"io"
 	"os"
-	"strings"
 
 	"github.com/grafana/tanka/pkg/kubernetes/manifest"
 	"github.com/pkg/errors"
@@ -67,6 +66,8 @@ type TemplateOpts struct {
 	// IncludeCRDs specifies whether CustomResourceDefinitions are included in
 	// the template output
 	IncludeCRDs bool
+	// Kubernetes version used for Capabilities.KubeVersion
+	KubeVersion string
 	// Namespace scope for this request
 	Namespace string
 	// NoHooks specifies whether hooks should be excluded from the template output
@@ -78,8 +79,7 @@ type TemplateOpts struct {
 func (t TemplateOpts) Flags() []string {
 	var flags []string
 
-	if t.APIVersions != nil {
-		value := strings.Join(t.APIVersions, ",")
+	for _, value := range t.APIVersions {
 		flags = append(flags, "--api-versions="+value)
 	}
 
@@ -87,6 +87,10 @@ func (t TemplateOpts) Flags() []string {
 		flags = append(flags, "--include-crds")
 	}
 
+	if t.KubeVersion != "" {
+		flags = append(flags, "--kube-version="+t.KubeVersion)
+	}
+
 	if t.NoHooks {
 		flags = append(flags, "--no-hooks")
 	}
diff --git a/pkg/tanka/evaluators.go b/pkg/tanka/evaluators.go
index 927419e3..9e9338c3 100644
--- a/pkg/tanka/evaluators.go
+++ b/pkg/tanka/evaluators.go
@@ -21,7 +21,7 @@ func evalJsonnet(path string, opts jsonnet.Opts) (raw string, err error) {
 	if opts.EvalScript != "" {
 		var tla []string
 		for k := range opts.TLACode {
-			tla = append(tla, k)
+			tla = append(tla, k+"="+k)
 		}
 		evalScript := fmt.Sprintf(`
   local main = (import '%s');
diff --git a/pkg/tanka/export.go b/pkg/tanka/export.go
index 8916bd0a..69109595 100644
--- a/pkg/tanka/export.go
+++ b/pkg/tanka/export.go
@@ -175,7 +175,7 @@ func writeExportFile(path string, data []byte) error {
 
 func createTemplate(format string, env manifest.Manifest) (*template.Template, error) {
 	// Replace all os.path separators in string with BelRune for creating subfolders
-	replaceFormat := strings.Replace(format, string(os.PathSeparator), BelRune, -1)
+	replaceFormat := replaceTmplText(format, string(os.PathSeparator), BelRune)
 
 	envMap := template.FuncMap{"env": func() manifest.Manifest { return env }}
 
@@ -189,6 +189,24 @@ func createTemplate(format string, env manifest.Manifest) (*template.Template, e
 	return template, nil
 }
 
+func replaceTmplText(s, old, new string) string {
+	parts := []string{}
+	l := strings.Index(s, "{{")
+	r := strings.Index(s, "}}") + 2
+
+	for l != -1 && l < r {
+		// replace only in text between template action blocks
+		text := strings.ReplaceAll(s[:l], old, new)
+		action := s[l:r]
+		parts = append(parts, text, action)
+		s = s[r:]
+		l = strings.Index(s, "{{")
+		r = strings.Index(s, "}}") + 2
+	}
+	parts = append(parts, strings.ReplaceAll(s, old, new))
+	return strings.Join(parts, "")
+}
+
 func applyTemplate(template *template.Template, m manifest.Manifest) (path string, err error) {
 	buf := bytes.Buffer{}
 	if err := template.Execute(&buf, m); err != nil {


Test patch:
diff --git a/pkg/tanka/evaluators_test.go b/pkg/tanka/evaluators_test.go
new file mode 100644
index 00000000..0d3013c9
--- /dev/null
+++ b/pkg/tanka/evaluators_test.go
@@ -0,0 +1,33 @@
+package tanka
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/grafana/tanka/pkg/jsonnet"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestEvalJsonnet(t *testing.T) {
+	var tlaCode jsonnet.InjectedCode
+	// Pass in the mandatory parameters as TLA codes, but note that only `foo`
+	// contains `data`, which is a valid key inside the `o` object defined in
+	// testdata/cases/withtlas/main.jsonnet. If they are passed as positional
+	// parameters, then their names are ignored, which will lead to arbitrary
+	// failures because the order in which they're passed is random.
+	// `evalJsonnet` has been updated to pass them as named parameters.
+	tlaCode.Set("foo", "'data'")
+	tlaCode.Set("bar", "'kaboom'")
+	tlaCode.Set("baz", "'kaboom'")
+
+	opts := jsonnet.Opts{
+		EvalScript: "main",
+		TLACode:    tlaCode,
+	}
+
+	// This will fail intermittently if TLAs are passed as positional
+	// parameters.
+	json, err := evalJsonnet("testdata/cases/withtlas", opts)
+	assert.NoError(t, err)
+	assert.Equal(t, `"foovalue"`, strings.TrimSpace(json))
+}
diff --git a/pkg/tanka/export_test.go b/pkg/tanka/export_test.go
new file mode 100644
index 00000000..9663c695
--- /dev/null
+++ b/pkg/tanka/export_test.go
@@ -0,0 +1,33 @@
+package tanka
+
+import "testing"
+
+func Test_replaceTmplText(t *testing.T) {
+	type args struct {
+		s   string
+		old string
+		new string
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{"text only", args{"a", "a", "b"}, "b"},
+		{"action blocks", args{"{{a}}{{.}}", "a", "b"}, "{{a}}{{.}}"},
+		{"mixed", args{"a{{a}}a{{a}}a", "a", "b"}, "b{{a}}b{{a}}b"},
+		{"invalid template format handled as text", args{"a}}a{{a", "a", "b"}, "b}}b{{b"},
+		{
+			name: "keep path separator in action block",
+			args: args{`{{index .metadata.labels "app.kubernetes.io/name"}}/{{.metadata.name}}`, "/", BelRune},
+			want: "{{index .metadata.labels \"app.kubernetes.io/name\"}}\u0007{{.metadata.name}}",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := replaceTmplText(tt.args.s, tt.args.old, tt.args.new); got != tt.want {
+				t.Errorf("replaceInTmplText() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/pkg/tanka/testdata/cases/withtlas/main.jsonnet b/pkg/tanka/testdata/cases/withtlas/main.jsonnet
new file mode 100644
index 00000000..c6442130
--- /dev/null
+++ b/pkg/tanka/testdata/cases/withtlas/main.jsonnet
@@ -0,0 +1,5 @@
+local o = {
+  data: 'foovalue',
+};
+
+function(foo, bar, baz) o[foo]