Skip to content

Commit a6bd101

Browse files
authored
Restor the kyaml fnsdk, which is used by the terraform kpt file (#1168)
Signed-off-by: liamfallon <[email protected]>
1 parent 22e9080 commit a6bd101

39 files changed

+3781
-0
lines changed

thirdparty/kyaml/fnsdk/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This package is intended to be upstreamed in sigs.k8s.io/kustomize/kyaml.
2+
PR: https://github.com/kubernetes-sigs/kustomize/pull/4319
3+
This location only serves as an intermediate location before that PR merged.

thirdparty/kyaml/fnsdk/doc.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2021 The Kubernetes Authors.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/*
5+
Package fnsdk provides an SDK for writing KRM functions in Go. The function
6+
specification is defined at:
7+
https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
8+
9+
Note: this package is an alpha package.
10+
11+
A KRM functions can generate, mutate or validate Kubernetes resources in a
12+
ResourceList.
13+
14+
The ResourceList type and the KubeObject type are the core part of this package.
15+
The ResourceList type maps to the ResourceList in the function spec. The
16+
KubeObject represent a kubernetes resource in a ResourceList, and it's the basic
17+
unit to perform most CRUD operations.
18+
19+
A KRM function does the following things:
20+
21+
1. read yaml bytes from stdin and convert it to a ResourceList
22+
2. perform mutation and validation on the resources in the ResourceList
23+
3. write the updated ResourceList out to stdout in yaml format
24+
4. Any diagnostic messages should be writen to stderr
25+
26+
ResourceListProcessor
27+
28+
In most cases, you only need to do #2 which is implementing a
29+
ResourceListProcessor and then pass it to AsMain. In the following example, we
30+
use ResourceListProcessorFunc that implements the ResourceListProcessor
31+
interface.
32+
33+
func main() {
34+
if err := fnsdk.AsMain(fnsdk.ResourceListProcessorFunc(myfn)); err != nil {
35+
os.Exit(1)
36+
}
37+
}
38+
39+
func myfn(rl *fnsdk.ResourceList) error {
40+
fnsdk.Log("log something")
41+
// mutate or validate the ResourceList
42+
}
43+
44+
KubeObject
45+
46+
KubeObject hides all the details about yaml.Node and yaml.RNode. It is always
47+
recommended converting a KubeObject to a strong typed object or getting a field
48+
as a strong typed object. Then do the CRUD operation on the strong typed objects.
49+
*/
50+
package fnsdk

thirdparty/kyaml/fnsdk/document.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package fnsdk
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"sigs.k8s.io/kustomize/kyaml/yaml"
7+
)
8+
9+
type doc struct {
10+
nodes []*yaml.Node
11+
}
12+
13+
func newDoc(nodes ...*yaml.Node) *doc {
14+
return &doc{nodes: nodes}
15+
}
16+
17+
func parseDoc(b []byte) (*doc, error) {
18+
br := bytes.NewReader(b)
19+
20+
var nodes []*yaml.Node
21+
decoder := yaml.NewDecoder(br)
22+
for {
23+
node := &yaml.Node{}
24+
if err := decoder.Decode(node); err != nil {
25+
if err == io.EOF {
26+
break
27+
}
28+
return nil, err
29+
}
30+
nodes = append(nodes, node)
31+
}
32+
33+
return &doc{nodes: nodes}, nil
34+
}
35+
36+
func (d *doc) ToYAML() ([]byte, error) {
37+
var w bytes.Buffer
38+
encoder := yaml.NewEncoder(&w)
39+
for _, node := range d.nodes {
40+
if node.Kind == yaml.DocumentNode {
41+
if len(node.Content) == 0 {
42+
// These cause errors when we try to write them
43+
continue
44+
}
45+
}
46+
if err := encoder.Encode(node); err != nil {
47+
return nil, err
48+
}
49+
}
50+
51+
return w.Bytes(), nil
52+
}
53+
54+
func (d *doc) Objects() ([]*mapVariant, error) {
55+
return extractObjects(d.nodes...)
56+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package fnsdk_test
2+
3+
import (
4+
"os"
5+
6+
"github.com/kptdev/krm-functions-catalog/thirdparty/kyaml/fnsdk"
7+
)
8+
9+
// This example implements a function that updates the replicas field for all deployments.
10+
11+
func Example_filterGVK() {
12+
if err := fnsdk.AsMain(fnsdk.ResourceListProcessorFunc(updateReplicas)); err != nil {
13+
os.Exit(1)
14+
}
15+
}
16+
17+
// updateReplicas sets a field in resources selecting by GVK.
18+
func updateReplicas(rl *fnsdk.ResourceList) error {
19+
if rl.FunctionConfig == nil {
20+
return fnsdk.ErrMissingFnConfig{}
21+
}
22+
var replicas int
23+
rl.FunctionConfig.GetOrDie(&replicas, "replicas")
24+
for i, obj := range rl.Items {
25+
if obj.APIVersion() == "apps/v1" && obj.Kind() == "Deployment" {
26+
rl.Items[i].SetOrDie(replicas, "spec", "replicas")
27+
}
28+
}
29+
return nil
30+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package fnsdk_test
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"net/http"
7+
"os"
8+
9+
"github.com/kptdev/krm-functions-catalog/thirdparty/kyaml/fnsdk"
10+
corev1 "k8s.io/api/core/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
)
13+
14+
// This function generates Graphana configuration in the form of ConfigMap. It
15+
// accepts Revision and ID as input.
16+
17+
func Example_generator() {
18+
if err := fnsdk.AsMain(fnsdk.ResourceListProcessorFunc(generate)); err != nil {
19+
os.Exit(1)
20+
}
21+
}
22+
23+
// generate generates a ConfigMap.
24+
func generate(rl *fnsdk.ResourceList) error {
25+
if rl.FunctionConfig == nil {
26+
return fnsdk.ErrMissingFnConfig{}
27+
}
28+
29+
revision := rl.FunctionConfig.GetStringOrDie("data", "revision")
30+
id := rl.FunctionConfig.GetStringOrDie("data", "id")
31+
js, err := fetchDashboard(revision, id)
32+
if err != nil {
33+
return fmt.Errorf("fetch dashboard: %v", err)
34+
}
35+
36+
cm := corev1.ConfigMap{
37+
TypeMeta: metav1.TypeMeta{
38+
APIVersion: "v1",
39+
Kind: "ConfigMap",
40+
},
41+
ObjectMeta: metav1.ObjectMeta{
42+
Name: fmt.Sprintf("%v-gen", rl.FunctionConfig.Name()),
43+
Namespace: rl.FunctionConfig.Namespace(),
44+
Labels: map[string]string{
45+
"grafana_dashboard": "true",
46+
},
47+
},
48+
Data: map[string]string{
49+
fmt.Sprintf("%v.json", rl.FunctionConfig.Name()): fmt.Sprintf("%q", js),
50+
},
51+
}
52+
return rl.UpsertObjectToItems(cm, nil, false)
53+
}
54+
55+
func fetchDashboard(revision, id string) (string, error) {
56+
url := fmt.Sprintf("https://grafana.com/api/dashboards/%s/revisions/%s/download", id, revision)
57+
resp, err := http.Get(url)
58+
if err != nil {
59+
return "", err
60+
}
61+
defer resp.Body.Close()
62+
b, err := ioutil.ReadAll(resp.Body)
63+
if err != nil {
64+
return "", err
65+
}
66+
return string(b), nil
67+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package fnsdk_test
2+
3+
import (
4+
"os"
5+
6+
"github.com/kptdev/krm-functions-catalog/thirdparty/kyaml/fnsdk"
7+
corev1 "k8s.io/api/core/v1"
8+
"sigs.k8s.io/kustomize/kyaml/yaml"
9+
)
10+
11+
// In this example, we implement a function that injects a logger as a sidecar
12+
// container in workload APIs.
13+
14+
func Example_loggeInjector() {
15+
if err := fnsdk.AsMain(fnsdk.ResourceListProcessorFunc(injectLogger)); err != nil {
16+
os.Exit(1)
17+
}
18+
}
19+
20+
// injectLogger injects a logger container into the workload API resources.
21+
// generate implements the gofnsdk.KRMFunction interface.
22+
func injectLogger(rl *fnsdk.ResourceList) error {
23+
var li LoggerInjection
24+
if err := rl.FunctionConfig.As(&li); err != nil {
25+
return err
26+
}
27+
for i, obj := range rl.Items {
28+
if obj.APIVersion() == "apps/v1" && (obj.Kind() == "Deployment" || obj.Kind() == "StatefulSet" || obj.Kind() == "DaemonSet" || obj.Kind() == "ReplicaSet") {
29+
var containers []corev1.Container
30+
obj.GetOrDie(&containers, "spec", "template", "spec", "containers")
31+
foundTargetContainer := false
32+
for j, container := range containers {
33+
if container.Name == li.ContainerName {
34+
containers[j].Image = li.ImageName
35+
foundTargetContainer = true
36+
break
37+
}
38+
}
39+
if !foundTargetContainer {
40+
c := corev1.Container{
41+
Name: li.ContainerName,
42+
Image: li.ImageName,
43+
}
44+
containers = append(containers, c)
45+
}
46+
rl.Items[i].SetOrDie(containers, "spec", "template", "spec", "containers")
47+
}
48+
}
49+
return nil
50+
}
51+
52+
// LoggerInjection is type definition of the functionConfig.
53+
type LoggerInjection struct {
54+
yaml.ResourceMeta `json:",inline" yaml:",inline"`
55+
56+
ContainerName string `json:"containerName" yaml:"containerName"`
57+
ImageName string `json:"imageName" yaml:"imageName"`
58+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package fnsdk_test
2+
3+
import (
4+
"os"
5+
"strings"
6+
7+
"github.com/kptdev/krm-functions-catalog/thirdparty/kyaml/fnsdk"
8+
)
9+
10+
// In this example, we mutate line comments for field metadata.name.
11+
// Some function may want to store some information in the comments (e.g.
12+
// apply-setters function: https://catalog.kpt.dev/apply-setters/v0.2/)
13+
14+
func Example_dMutateComments() {
15+
if err := fnsdk.AsMain(fnsdk.ResourceListProcessorFunc(mutateComments)); err != nil {
16+
os.Exit(1)
17+
}
18+
}
19+
20+
func mutateComments(rl *fnsdk.ResourceList) error {
21+
for i := range rl.Items {
22+
lineComment, found, err := rl.Items[i].LineComment("metadata", "name")
23+
if err != nil {
24+
return err
25+
}
26+
if !found {
27+
return nil
28+
}
29+
30+
if strings.TrimSpace(lineComment) == "" {
31+
lineComment = "bar-system"
32+
} else {
33+
lineComment = strings.Replace(lineComment, "foo", "bar", -1)
34+
}
35+
if err = rl.Items[i].SetLineComment(lineComment, "metadata", "name"); err != nil {
36+
return err
37+
}
38+
}
39+
return nil
40+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package fnsdk_test
2+
3+
import (
4+
"os"
5+
6+
"github.com/kptdev/krm-functions-catalog/thirdparty/kyaml/fnsdk"
7+
)
8+
9+
// In this example, we read a field from the input object and print it to the log.
10+
11+
func Example_aReadField() {
12+
if err := fnsdk.AsMain(fnsdk.ResourceListProcessorFunc(readField)); err != nil {
13+
os.Exit(1)
14+
}
15+
}
16+
17+
func readField(rl *fnsdk.ResourceList) error {
18+
for _, obj := range rl.Items {
19+
if obj.APIVersion() == "apps/v1" && obj.Kind() == "Deployment" {
20+
var replicas int
21+
obj.GetOrDie(&replicas, "spec", "replicas")
22+
fnsdk.Logf("replicas is %v\n", replicas)
23+
}
24+
}
25+
return nil
26+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package fnsdk_test
2+
3+
import (
4+
"os"
5+
6+
"github.com/kptdev/krm-functions-catalog/thirdparty/kyaml/fnsdk"
7+
"sigs.k8s.io/kustomize/kyaml/yaml"
8+
)
9+
10+
// In this example, we convert the functionConfig as strong typed object and then
11+
// read a field from the functionConfig object.
12+
13+
func Example_bReadFunctionConfig() {
14+
if err := fnsdk.AsMain(fnsdk.ResourceListProcessorFunc(readFunctionConfig)); err != nil {
15+
os.Exit(1)
16+
}
17+
}
18+
19+
func readFunctionConfig(rl *fnsdk.ResourceList) error {
20+
var sr SetReplicas
21+
rl.FunctionConfig.AsOrDie(&sr)
22+
fnsdk.Logf("desired replicas is %v\n", sr.DesiredReplicas)
23+
return nil
24+
}
25+
26+
// SetReplicas is the type definition of the functionConfig
27+
type SetReplicas struct {
28+
yaml.ResourceIdentifier `json:",inline" yaml:",inline"`
29+
DesiredReplicas int `json:"desiredReplicas,omitempty" yaml:"desiredReplicas,omitempty"`
30+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package fnsdk_test
2+
3+
import (
4+
"os"
5+
6+
"github.com/kptdev/krm-functions-catalog/thirdparty/kyaml/fnsdk"
7+
)
8+
9+
// In this example, we read a field from the input object and print it to the log.
10+
11+
func Example_cSetField() {
12+
if err := fnsdk.AsMain(fnsdk.ResourceListProcessorFunc(setField)); err != nil {
13+
os.Exit(1)
14+
}
15+
}
16+
17+
func setField(rl *fnsdk.ResourceList) error {
18+
for _, obj := range rl.Items {
19+
if obj.APIVersion() == "apps/v1" && obj.Kind() == "Deployment" {
20+
replicas := 10
21+
obj.SetOrDie(&replicas, "spec", "replicas")
22+
}
23+
}
24+
return nil
25+
}

0 commit comments

Comments
 (0)