Skip to content
This repository has been archived by the owner on Feb 6, 2024. It is now read-only.

rules_k8s: Support passing in a kubeconfig file #287

Merged
merged 1 commit into from
Mar 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,15 @@ A rule for interacting with Kubernetes objects.
current context.</b></p>
</td>
</tr>
<tr>
<td><code>kubeconfig</code></td>
<td>
<p><code>kubeconfig file, optional</code></p>
<p>The kubeconfig file to pass to the `kubectl` tool via the
`--kubeconfig` option. Can be useful if the `kubeconfig` is generated
by another target.</p>
</td>
</tr>
<tr>
<td><code>substitutions</code></td>
<td>
Expand Down
14 changes: 14 additions & 0 deletions examples/hellogrpc/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ k8s_deploy(
template = "deployment.yaml",
)

k8s_deploy(
name = "staging-deployment-with-kubeconfig",
kubeconfig = ":kubeconfig",
template = "deployment.yaml",
)

k8s_service(
name = "staging-service",
substitutions = {
Expand Down Expand Up @@ -78,6 +84,14 @@ jsonnet_to_json(
],
)

# Generate an empty kubeconfig for testing
genrule(
name = "kubeconfig",
srcs = [],
outs = ["kubeconfig.out"],
cmd = "touch $@",
)

# Verify that deployment.json == deployment.yaml
py_test(
name = "deployment_test",
Expand Down
24 changes: 24 additions & 0 deletions examples/hellogrpc/e2e-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ function EXPECT_CONTAINS() {
CONTAINS "${complete}" "${substring}" || fail "$message"
}

function CONTAINS_PATTERN() {
local complete="${1}"
local substring="${2}"

echo "${complete}" | grep -Esq -- "${substring}"
}

function EXPECT_CONTAINS_PATTERN() {
local complete="${1}"
local substring="${2}"
local message="${3:-Expected '${substring}' not found in '${complete}'}"

echo Checking "$1" contains pattern "$2"
CONTAINS_PATTERN "${complete}" "${substring}" || fail "$message"
}

# Ensure there is an ip address for hell-grpc-staging:50051
apply-lb() {
echo Applying service...
Expand Down Expand Up @@ -97,6 +113,12 @@ delete() {
bazel-bin/examples/hellogrpc/${LANGUAGE}/server/staging.delete
}

check_kubeconfig_args() {
bazel build examples/hellogrpc:staging-deployment-with-kubeconfig.apply
OUTPUT="$(cat ./bazel-bin/examples/hellogrpc/staging-deployment-with-kubeconfig.apply)"
EXPECT_CONTAINS_PATTERN "${OUTPUT}" "--kubeconfig=\S*/examples/hellogrpc/kubeconfig.out"
}

# e2e test that checks that args are added to the kubectl apply command
check_kubectl_args() {
# Checks that bazel run <some target> does pick up the args attr and
Expand All @@ -108,6 +130,8 @@ check_kubectl_args() {
EXPECT_CONTAINS "$(bazel run examples/hellogrpc:staging.apply -- --v=1)" "apply --v=2 --v=1"
}

check_kubeconfig_args

check_kubectl_args

apply-lb
Expand Down
3 changes: 2 additions & 1 deletion k8s/apply.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ function exe() { echo "\$ ${@/eval/}" ; "$@" ; }
RUNFILES="${PYTHON_RUNFILES:-$(guess_runfiles)}"

PYTHON_RUNFILES=${RUNFILES} %{resolve_script} | \
exe %{kubectl_tool} --cluster="%{cluster}" --context="%{context}" --user="%{user}" %{namespace_arg} apply $@ -f -
exe %{kubectl_tool} --kubeconfig="%{kubeconfig}" --cluster="%{cluster}" \
Copy link
Contributor

Choose a reason for hiding this comment

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

What does kubectl do when --kubeconfig is given a blank string? Would it be preferable to only specify --kubeconfig when there's something to specify? The if condition would move to the k8s_object rule.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When --kubeconfig='', the behavior is the same as not specifying --kubeconfig at all. It's the same as --cluster='' and --context='' in that regard.

I agree that it would be cleaner and preferable to not output flags if the value is empty, since we're just getting lucky that the default value of all the current flags is an empty string. However, I think that should be a separate PR that would do this for all of the flags, rather than having different behavior for just this one flag.

--context="%{context}" --user="%{user}" %{namespace_arg} apply $@ -f -
3 changes: 2 additions & 1 deletion k8s/create.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ RUNFILES="${PYTHON_RUNFILES:-$(guess_runfiles)}"
# TODO(mattmoor): Should we create namespaces that do not exist?

PYTHON_RUNFILES=${RUNFILES} %{resolve_script} | \
exe %{kubectl_tool} --cluster="%{cluster}" --context="%{context}" --user="%{user}" %{namespace_arg} create $@ -f -
exe %{kubectl_tool} --kubeconfig="%{kubeconfig}" --cluster="%{cluster}" \
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment here as apply

--context="%{context}" --user="%{user}" %{namespace_arg} create $@ -f -
16 changes: 16 additions & 0 deletions k8s/object.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ def _common_impl(ctx):
if namespace_arg:
namespace_arg = "--namespace=\"" + namespace_arg + "\""

if ctx.file.kubeconfig:
kubeconfig_arg = _runfiles(ctx, ctx.file.kubeconfig)
files += [ctx.file.kubeconfig]
else:
kubeconfig_arg = ""

kubectl_tool_info = ctx.toolchains["@io_bazel_rules_k8s//toolchains/kubectl:toolchain_type"].kubectlinfo
if kubectl_tool_info.tool_path == "" and not kubectl_tool_info.tool_target:
# If tool_path is empty and tool_target is None then there is no local
Expand All @@ -200,6 +206,7 @@ def _common_impl(ctx):
"%{cluster}": cluster_arg,
"%{context}": context_arg,
"%{kind}": ctx.attr.kind,
"%{kubeconfig}": kubeconfig_arg,
"%{kubectl_tool}": kubectl_tool,
"%{namespace_arg}": namespace_arg,
"%{user}": user_arg,
Expand Down Expand Up @@ -235,6 +242,9 @@ _common_attrs = {
"image_chroot": attr.string(),
# This is only needed for describe.
"kind": attr.string(),
"kubeconfig": attr.label(
allow_single_file = True,
),
"namespace": attr.string(),
"resolver": attr.label(
default = Label("//k8s:resolver"),
Expand Down Expand Up @@ -455,6 +465,7 @@ def k8s_object(name, **kwargs):
cluster: the name of the cluster.
user: the user which has access to the cluster.
namespace: the namespace within the cluster.
kubeconfig: the kubeconfig file to use with kubectl.
kind: the object kind.
template: the yaml template to instantiate.
images: a dictionary from fully-qualified tag to label.
Expand Down Expand Up @@ -482,6 +493,7 @@ def k8s_object(name, **kwargs):
kind = kwargs.get("kind"),
cluster = kwargs.get("cluster"),
context = kwargs.get("context"),
kubeconfig = kwargs.get("kubeconfig"),
user = kwargs.get("user"),
namespace = kwargs.get("namespace"),
args = kwargs.get("args"),
Expand All @@ -493,6 +505,7 @@ def k8s_object(name, **kwargs):
kind = kwargs.get("kind"),
cluster = kwargs.get("cluster"),
context = kwargs.get("context"),
kubeconfig = kwargs.get("kubeconfig"),
user = kwargs.get("user"),
namespace = kwargs.get("namespace"),
args = kwargs.get("args"),
Expand All @@ -504,6 +517,7 @@ def k8s_object(name, **kwargs):
kind = kwargs.get("kind"),
cluster = kwargs.get("cluster"),
context = kwargs.get("context"),
kubeconfig = kwargs.get("kubeconfig"),
user = kwargs.get("user"),
namespace = kwargs.get("namespace"),
args = kwargs.get("args"),
Expand All @@ -515,6 +529,7 @@ def k8s_object(name, **kwargs):
kind = kwargs.get("kind"),
cluster = kwargs.get("cluster"),
context = kwargs.get("context"),
kubeconfig = kwargs.get("kubeconfig"),
user = kwargs.get("user"),
namespace = kwargs.get("namespace"),
args = kwargs.get("args"),
Expand All @@ -527,6 +542,7 @@ def k8s_object(name, **kwargs):
kind = kwargs.get("kind"),
cluster = kwargs.get("cluster"),
context = kwargs.get("context"),
kubeconfig = kwargs.get("kubeconfig"),
user = kwargs.get("user"),
namespace = kwargs.get("namespace"),
args = kwargs.get("args"),
Expand Down