Can I create a file or replace content of a file using Kubernetes?

1/10/2020

I have a react application that is hosted in a nginx container using static files that are prepared in a build step. The problem I run in to is that the API URL is then hard coded in the js files and I get a problem when I want to deploy the application to different environments.

So basically I have put a config.js file with the localhost API URL variable in the public directory which is then loaded in the application in the section of the index.html file. This works for the local environment. The problem comes when I want to deploy it to the test or production environment.

I have found out that it is possible to use a configMap with volume mounts, but that requires me to prepare one file for each environment in advance as I understand it. I want to be able to use the variables I have set in my Azure DevOps library to populate the API URL value.

So my question is if there is a way to replace the values in the config.js file in the nginx container using Kuberentes/Helm or if I can make use of a Azure DevOps pipeline task to replace the content of a pre-prepared config.js file and mount that using Kubernetes?

Not sure if it is clear what I want to do, but hopefully you can understand it...

config.js

window.env = {
    API_URL: 'http://localhost:8080'
};

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <title>My application</title>
    <!--
      config.js provides all environment specific configuration used in the client
    -->
    <script src="%PUBLIC_URL%/config.js"></script>
  </head>
  ...
-- KungWaz
azure-devops
kubernetes

3 Answers

1/16/2020

What I ended up doing was setting it up like this:

First I added a configmap.yaml to generate the config.js file

apiVersion: v1
kind: ConfigMap
metadata:
  name: config-frontend
data:
  config.js: |-
    window.env = {
      API_URL: "{{ .Values.service.apiUrl }}"
    }

Values.service.apiUrl comes from the arguments provided in the "Package and deploy Helm charts" task --set service.apiUrl=$(backend.apiUrl)

Then I added a volume mount in the deployment.yaml to replace the config.js file in the nginx container

...
containers:
  ...
  volumeMounts:
    - name: config-frontend-volume
      readOnly: true
      mountPath: "/usr/share/nginx/html/config.js"
      subPath: "config.js"
volumes:
  - name: config-frontend-volume
    configMap:
      name: config-frontend

This did the trick and now I can control the variable from the Azure DevOps pipeline based on the environment I'm deploying to.

-- KungWaz
Source: StackOverflow

1/10/2020

You can achieve this in several ways. Following are the few.

1.ConfigMap

Most effective and best way to achieve this, like one of the added comments. You can do something like this with a single config map.

Example ConfigMap might look something like this

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Values.definitionName }}-{{ .Values.envName }}-configmap
  namespace: {{ .Values.Namespace }}
data:
  API_URL: '{{ pluck .Values.envName .Values.API_URL | first }}'

Example Values file in helm charts would look like this

API_URL:
  dev: dev.mycompany.io
  staging: staging.mycompany.io
  test: test.mycompany.io
  prod: mycompany.io

And before helm install or helm upgrade run add a step in Azure devOps to run the bash command on your CI/CD pipeline, but make sure you have yq tool installed to do the thing. Or you can use any tool to do the same.

yq w -i values.yaml envName dev

This whole process replaces your config file with API_URL to dev.mycompany.io as I gave dev in yq tool.

But if you are confused with using yq tool or something, you can have multiple values files for each environment separately and make changes to helm install step in your deployment.

helm install ./path --values ./dev-values.yaml

But your configmap should look something like this if you have multiple values files and operating which values to pick from helm install

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Values.definitionName }}-{{ .Values.envName }}-configmap
  namespace: {{ .Values.Namespace }}
data:
  API_URL: '{{ .Values.API_URL }}'

Well this is one way of doing things.

2.Manipulating Dockerfile

You can also do this with dockerfile, something like this step in your dockerfile would replace the value of the file.

RUN sed -i "s/env/dev.mycompany.io/" /app/config.js

But as the url is unique to each env you can take values using ARG

ARG url

RUN sed -i "s/env/${url}" /app/config.js

And during your build pipeline you need to have a task for docker build and under that pass the value of url as an argument you can see that arguments column in your task add this --build-arg url=dev.mycompany.io

This is another way to add values to your config.js file, but it also adds four(based on four envs) docker builds. And so your agents would be busy building four different images for each git commit and queuing up others builds. If you feel that command is not working in Dockerfile add RUN cat /app/config.js in your docker file, and you can debug what's happening and check if the values are updated as you change.

Again it's debatable which is good and bad, but I personally prefer first one due to number of commits I make in an hour, but if the url changes you need not change your codebase you just need to update the docker build in your pipeline. So kinda debatable.

There are other ways to do this as well. But these two are somewhat simplest to achieve.

Hope this is helpful.

-- BinaryMonster
Source: StackOverflow

1/13/2020

In addition to the method of @BinaryBullet provided, you can try with another way that it can make use of one Azure DevOps task to replace the content of config.js file before this .js is applied with kubernetes.

Replace Tokens


The use of this task is very simple.

Step1:

Configure yourself Token prefix:

enter image description here

Step2:

Then apply this Token prefix into your config.js file where you want it be replaced by various values dynamically:

enter image description here

Step3:

Do not forget to specify the value you want it passed to config.js into Variables tab:

enter image description here

Note: The variable name must same with the one you configured in config.js. During the task running, it will inject the corresponding variable value into the config.js file based on the replace format #{}# and same variable name.

For example, I use apiurl in my second screenshots, so here I add one variable apiurl and give it value which I want this value can be replaced into this config.js file at build time.


Build result:

enter image description here

This Replace token task do not has limitation. It can be used in various type file. See my another similar answer: #1.

Hope this is the one which can help you achieve your expectation.

-- Merlin Liang - MSFT
Source: StackOverflow