It's a simple question, but I could not find a definite answer for it. Is it possible to create a namespace only if it doesn't exist. My objective is to create some service accounts without caring if their namespaces exist or not (if not, then they should be created on the fly).
The thing is I'm using CDK to deploy some basics K8S resources (including service accounts). To safely do this, I need to make sure the namespace (given in the service account manifest) already exists. However I'm not able to find any solution.
I tried patch
, but it seems to expect the resource to exist already (i.e. it fails with NotFound
error).
Two limitations:
a. I can't query to see if the namespace exists or not.
b. I can't use apply
since I don't have the exact definition of the namespace.
Is there any way to achieve this?
i wouldn't go for any other solution except the following code snippet:
kubernetes 1.19 and above (included)
kubectl create namespace <add-namespace-here> --dry-run=client -o yaml | kubectl apply -f -
kubernetes 1.18 and below (included)
kubectl create namespace <add-namespace-here> --dry-run -o yaml | kubectl apply -f -
it creates a namespace in dry-run and outputs it as a yaml. The output will be passed as stdin to kubectl apply -f -
The last hyphen is important while passing kubectl to read from stdin.
Also see the examples in:
kubectl apply --help
Given the limitations I can only think of one way which is to apply a namespace yaml always before you apply the service account yaml. If the namespace exists already it will give you a message that namespace already exists
.You can ignore that message and move ahead.
apiVersion: v1
kind: Namespace
metadata:
name: test
So here we are being declarative and it does not matter what exists and what does not. You just define what the desired state should look like and kubernetes will take care of making sure that happens.
The options highlighted by @Panoptik and @Arghya Sadhu got me to use this one liner in a deployment pipeline:
echo -e "apiVersion: v1\nkind: Namespace\nmetadata:\n name: ${NS_NAME}" | kubectl
apply -f -
Why an one liner: I needed to avoid line breaks in the pipeline. The code was tested on Debian and also the official Google Cloud Build image "gcloud".
You can also consider using helm for this. Helm has a feature that creates the namespace for you if it doesn't exist and it simplifies the deployment of whatever app you want to deploy into that namespace. Notice the use of "--create-namespace", this will create my-namespace for you.
helm -n my-namespace upgrade --install --create-namespace my-app my-app-folder/
Tips:
I have tried most of the options but the latest works for my deployment script best:
if ! kubectl get namespaces -o json | jq -r ".items[].metadata.name" | grep staging;then
echo 'HALLO'
fi
It is not the answer to specified question, but it is ready to use solution for those who google for subject question.
Based on @Arghya Sadhu answer my bash solution for creating if not exist namespace looks next:
echo "apiVersion: v1
kind: Namespace
metadata:
name: ${NS_NAME}" | kubectl apply -f -
I was able to solve this problem using,
myNamespace="new-namespace"
kubectl get namespace | grep -q "^$myNamespace " || kubectl create namespace $myNamespace
The command kubectl get namespace
gives an output like,
NAME STATUS AGE
alpha Active 29m
default Active 112m
gatekeeper-system Active 111m
kube-node-lease Active 112m
kube-public Active 112m
kube-system Active 112m
some-branch Active 26m
something Active 7m28s
something-else Active 5m7s
Then, | grep -q "^$my-namespace "
will look for your namespace in the output. The q
will cause the command to return a 0
if your namespace is found. Otherwise it'll return a 1
.
Note: the ^
the beginning and white-space at the end are important. This ensures the whole namespace is matched, and not just part of it. For example, if you were searching for the namespace something
and did NOT include the space at the end, it would match both something
and something-else
from the example above.
Finally, || kubectl create namespace $my-namespace
will create the namespace if it was found (i.e. the grep returned 1
). Otherwise, it will not be created.
I mostly agree with @arghya-sadhu so far as declarative is nearly always the way to go. However, you could test for the existance of a namespace in bash, something like this:
#!/bin/bash
NAMESPACE_NAME=${1:-default};
export KUBECONFIG=your-kubeconfig.yml;
NS=$(kubectl get namespace $NAMESPACE_NAME --ignore-not-found);
if [[ "$NS" ]]; then
echo "Skipping creation of namespace $NAMESPACE_NAME - already exists";
else
echo "Creating namespace $NAMESPACE_NAME";
kubectl create namespace $NAMESPACE_NAME;
fi;