Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds kube_nodes_provider starlark built-in
This patch includes the implementation of the kube_nodes_provider starlark built-in. This provider allows listing of the hosts in a k8s cluster accessible by the provided/default kube config. It also includes identification of the provider in the resources directive, so that the crashd config script can use the kube_nodes_provider to enumerate the node IP addresses of the hosts to run crashd commands on.
- Loading branch information
Showing
8 changed files
with
269 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// Copyright (c) 2020 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package starlark | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/vmware-tanzu/crash-diagnostics/k8s" | ||
coreV1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
|
||
"go.starlark.net/starlark" | ||
"go.starlark.net/starlarkstruct" | ||
) | ||
|
||
// KubeNodesProviderFn is a built-in starlark function that collects compute resources from a k8s cluster | ||
// Starlark format: kube_nodes_provider([kube_config=kube_config(), ssh_config=ssh_config(), names=["foo", "bar], labels=["bar", "baz"]]) | ||
func KubeNodesProviderFn(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { | ||
|
||
structVal, err := kwargsToStruct(kwargs) | ||
if err != nil { | ||
return starlark.None, err | ||
} | ||
|
||
return newKubeNodesProvider(thread, structVal) | ||
} | ||
|
||
// newKubeNodesProvider returns a struct with k8s cluster node provider info | ||
func newKubeNodesProvider(thread *starlark.Thread, structVal *starlarkstruct.Struct) (*starlarkstruct.Struct, error) { | ||
kubeconfig, err := getKubeConfigPath(thread, structVal) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to kubeconfig") | ||
} | ||
client, err := k8s.New(kubeconfig) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "could not initialize search client") | ||
} | ||
|
||
searchParams := generateSearchParams(structVal) | ||
nodes, err := getNodes(client, searchParams.Names(), searchParams.Labels()) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "could not fetch nodes") | ||
} | ||
|
||
// dictionary for node provider struct | ||
kubeNodesProviderDict := starlark.StringDict{ | ||
"kind": starlark.String(identifiers.kubeNodesProvider), | ||
"transport": starlark.String("ssh"), | ||
} | ||
|
||
// add node info to dictionary | ||
var nodeIps []starlark.Value | ||
for _, node := range nodes { | ||
nodeIps = append(nodeIps, starlark.String(getNodeInternalIP(node))) | ||
} | ||
kubeNodesProviderDict["hosts"] = starlark.NewList(nodeIps) | ||
|
||
// add ssh info to dictionary | ||
if _, ok := kubeNodesProviderDict[identifiers.sshCfg]; !ok { | ||
data := thread.Local(identifiers.sshCfg) | ||
sshcfg, ok := data.(*starlarkstruct.Struct) | ||
if !ok { | ||
return nil, fmt.Errorf("%s: default ssh_config not found", identifiers.kubeNodesProvider) | ||
} | ||
kubeNodesProviderDict[identifiers.sshCfg] = sshcfg | ||
} | ||
|
||
return starlarkstruct.FromStringDict(starlarkstruct.Default, kubeNodesProviderDict), nil | ||
} | ||
|
||
func generateSearchParams(structVal *starlarkstruct.Struct) k8s.SearchParams { | ||
// change nodes key to names | ||
if _, err := structVal.Attr("nodes"); err == nil { | ||
dict := starlark.StringDict{} | ||
structVal.ToStringDict(dict) | ||
|
||
dict["names"] = dict["nodes"] | ||
structVal = starlarkstruct.FromStringDict(starlarkstruct.Default, dict) | ||
} | ||
return k8s.NewSearchParams(structVal) | ||
} | ||
|
||
func getNodes(k8sc *k8s.Client, names, labels string) ([]*coreV1.Node, error) { | ||
nodeResults, err := k8sc.Search( | ||
"core", // group | ||
"nodes", // kind | ||
"", // namespaces | ||
"", // version | ||
names, | ||
labels, | ||
"", // containers | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// collate | ||
var nodes []*coreV1.Node | ||
for _, result := range nodeResults { | ||
for _, item := range result.List.Items { | ||
node := new(coreV1.Node) | ||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, &node); err != nil { | ||
return nil, err | ||
} | ||
nodes = append(nodes, node) | ||
} | ||
} | ||
return nodes, nil | ||
} | ||
|
||
func getNodeInternalIP(node *coreV1.Node) (ipAddr string) { | ||
for _, addr := range node.Status.Addresses { | ||
if addr.Type == "InternalIP" { | ||
ipAddr = addr.Address | ||
return | ||
} | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Copyright (c) 2020 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package starlark | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"go.starlark.net/starlark" | ||
"go.starlark.net/starlarkstruct" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("kube_nodes_provider", func() { | ||
var ( | ||
executor *Executor | ||
err error | ||
) | ||
|
||
execSetup := func(crashdScript string) error { | ||
executor = New() | ||
return executor.Exec("test.kube.nodes.provider", strings.NewReader(crashdScript)) | ||
} | ||
|
||
It("returns a struct with the list of k8s nodes", func() { | ||
crashdScript := fmt.Sprintf(` | ||
kube_config(path="%s") | ||
ssh_config(username="uname", private_key_path="path") | ||
provider = kube_nodes_provider()`, k8sconfig) | ||
err = execSetup(crashdScript) | ||
Expect(err).NotTo(HaveOccurred()) | ||
|
||
data := executor.result["provider"] | ||
Expect(data).NotTo(BeNil()) | ||
|
||
provider, ok := data.(*starlarkstruct.Struct) | ||
Expect(ok).To(BeTrue()) | ||
|
||
val, err := provider.Attr("hosts") | ||
Expect(err).NotTo(HaveOccurred()) | ||
|
||
list := val.(*starlark.List) | ||
Expect(list.Len()).To(Equal(1)) | ||
}) | ||
|
||
It("returns a struct with ssh config", func() { | ||
crashdScript := fmt.Sprintf(` | ||
cfg = kube_config(path="%s") | ||
kube_config(path="/foo/bar") | ||
ssh_config(username="uname", private_key_path="path") | ||
provider = kube_nodes_provider(kube_config=cfg)`, k8sconfig) | ||
err = execSetup(crashdScript) | ||
Expect(err).NotTo(HaveOccurred()) | ||
|
||
data := executor.result["provider"] | ||
Expect(data).NotTo(BeNil()) | ||
|
||
provider, ok := data.(*starlarkstruct.Struct) | ||
Expect(ok).To(BeTrue()) | ||
|
||
sshCfg, err := provider.Attr(identifiers.sshCfg) | ||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(sshCfg).NotTo(BeNil()) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// Copyright (c) 2020 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package starlark | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"go.starlark.net/starlark" | ||
"go.starlark.net/starlarkstruct" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("resources with kube_nodes_provider()", func() { | ||
|
||
It("populates the resources with the cluster nodes as hosts", func() { | ||
crashdScript := fmt.Sprintf(` | ||
cfg = kube_config(path="%s") | ||
ssh_config(username="uname", private_key_path="path") | ||
res = resources(provider=kube_nodes_provider(kube_config=cfg))`, k8sconfig) | ||
|
||
executor := New() | ||
err := executor.Exec("test.resources.kube.nodes.provider", strings.NewReader(crashdScript)) | ||
Expect(err).NotTo(HaveOccurred()) | ||
|
||
data := executor.result["res"] | ||
Expect(data).NotTo(BeNil()) | ||
|
||
resources, ok := data.(*starlark.List) | ||
Expect(ok).To(BeTrue()) | ||
Expect(resources.Len()).To(Equal(1)) | ||
|
||
resStruct, ok := resources.Index(0).(*starlarkstruct.Struct) | ||
Expect(ok).To(BeTrue()) | ||
|
||
val, err := resStruct.Attr("kind") | ||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(trimQuotes(val.String())).To(Equal(identifiers.hostResource)) | ||
|
||
transport, err := resStruct.Attr("transport") | ||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(trimQuotes(transport.String())).To(Equal("ssh")) | ||
|
||
sshCfg, err := resStruct.Attr(identifiers.sshCfg) | ||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(sshCfg).NotTo(BeNil()) | ||
|
||
host, err := resStruct.Attr("host") | ||
Expect(err).NotTo(HaveOccurred()) | ||
// Regex to match IP address of the host | ||
Expect(trimQuotes(host.String())).To(MatchRegexp("^([1-9]?[0-9]{2}\\.)([0-9]{1,3}\\.){2}[0-9]{1,3}$")) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters