44

It's a simple question, but I could not find a definite answer for it.

Question

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:

  1. I can't query to see if the namespace exists or not.
  2. I can't use apply since I don't have the exact definition of the namespace.

Is there any way to achieve this?

Yoooda
  • 31
  • 2
  • 7
Rad
  • 4,292
  • 8
  • 33
  • 71

13 Answers13

93

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
Yennefer
  • 5,704
  • 7
  • 31
  • 44
Manuel
  • 1,928
  • 2
  • 16
  • 26
  • 5
    --dry-run is deprecated and can be replaced with --dry-run=client – Rad Dec 22 '20 at 16:44
  • I see. I still use 1.16. Thank you for sharing. – Manuel Dec 22 '20 at 20:35
  • 4
    Perhaps if you exclaim "I wouldn't go for any other solution except mine..." you should provide a reason why. We're using `helm upgrade` to deploy so adding `--create-namespace` was the easiest, most suitable solution. – occasl Nov 02 '21 at 22:24
  • Your solution is not wrong, but not everyone is using helm. – Manuel Nov 03 '21 at 06:23
  • 1
    Why we should have such overhead at 2021? – Vasiliy Shakhunov Nov 22 '21 at 10:29
  • 1
    Note, if there is an existing NS you want to treat with this command, it's throwing `Warning: resource namespaces/ccs-bapi is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.` - k8s 1.26 – Jarek Mar 22 '23 at 08:19
12

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.

Arghya Sadhu
  • 41,002
  • 9
  • 78
  • 107
  • You mean `create` or `apply`? – Rad Jul 28 '20 at 14:07
  • Right, sadly that means the basic/minimal definition is gonna overwrite the existing definition. No? – Rad Jul 28 '20 at 14:24
  • 1
    Yes..but that's a good thing because if there is a change you want it to be applied and override the old one isn't it? if there is no change nothing will change – Arghya Sadhu Jul 28 '20 at 14:26
  • Hm, I guess my case is kinda exception. If the namespace exists, I don't want to touch it. (Something like `CREATE TABLE IF NOT EXISTS ...` in SQL) – Rad Jul 28 '20 at 14:31
10

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.

Corey P
  • 955
  • 1
  • 14
  • 23
5

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".

MarcoK
  • 51
  • 1
  • 3
4

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:

  • Always use upgrade --install because it can do both those things
  • Use the option --set to set specific values in values.yaml at runtime of the command (useful i.e for secrets)
dom
  • 111
  • 5
3

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
Yuriy
  • 69
  • 2
3

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 -
Panoptik
  • 1,094
  • 1
  • 16
  • 23
2

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;
Fraser Goffin
  • 121
  • 1
  • 1
  • 5
2
NS_NAME=user3 && \
  kubectl create namespace $NS_NAME --dry-run=server && \
    echo "$NS_NAME namespace does not exist yet, creating it..." && \
      kubectl create namespace $NS_NAME

Explanation

Testing for existence can be achieved by attempting to create the new namespace, but only with --dry-run=server (caution: this is the only way to check if the namespace already exists, as --try-run=client would give false negatives). Then, if the dry run creation does NOT fail, we can create it for real (the chaining is done using &&, that requires that the previous command completed successfully, which it would only if the namespace did not exist).

mirekphd
  • 4,799
  • 3
  • 38
  • 59
1
kubectl create namespace <add-namespace-here> || echo "namespace already exists"

If the first part of the command fails, most likely it would mean it already exists. By using || instead of | you won't get your script interrupted by the error

another idea is to try to describe it, it will fail if it doesn't exist. But you said you can't query

kubectl describe ns <add-namespace-here> || kubectl create namespace <add-namespace-here>
chriscatfr
  • 2,592
  • 3
  • 24
  • 32
0

If you're using bash and just want to pipe any warnings that the namespace already exists when trying to create it you can pipe stderr to /dev/null

kubectl create namespace <namespace> 2>/dev/null
  • I think the answer is plain wrong, because the question specifically says 'if not exists'. The command tries to create it even if it exists, which will return a non-zero code. Not very useful in scripts, regardless what you do with the warning. – Lethargos Aug 31 '22 at 19:18
  • at the same time he said he can't query to see if the namespace exists or not. I don't see how to avoid the create command to see if it returns an error. – chriscatfr Apr 11 '23 at 11:59
0

-q did not work for me but having -c worked below is the output

kubectl get ns | grep -c "^too " || kubectl create namespace too

First execution

$ kubectl get ns | grep -c "^too " || kubectl create namespace too
0
namespace/too created

Second execution

$ kubectl get ns | grep -c "^too " || kubectl create namespace too
1

The use-case where we needed just so people know is when you need to create a new namespace and inject it to istio before you install any charts or services etc.

Amol Ghotankar
  • 2,008
  • 5
  • 28
  • 42
-3
kubectl create namespace <add-namespace-here> --dry-run=client -o yaml | kubectl apply -f -

This is solution from Arghya Sadhu an elegant. Although create is not a desired state, apply is. Thank you Arghya.