<p>I have an AKS cluster with a Node.js server and a mongo db. I want to create MongoDb records from a repair catalog in a json file using the server endpoint.</p>
<p>To do so I created an Helm chart , with the <code>sendRepiarData.js</code> and the <code>catalog.json</code> files under a <code>files</code> folder and a <code>Job</code> which should create a Node pod and run the <code>sendRepiarData.js</code> file.</p>
<p>When I install the chart the Job does create the pod and set the status to completed but no data is sent to the server and I don't get any logs from the pod when I run the <code>kubectl logs</code> command on that pod.</p>
<p>So to debug it I commented out the function and just printed to console, but still no logs from the pod. I'm fairly new to Kubernetes and I guess I'm misconfiguring the Job's volumeMounts a volumes parameters.
Can you spot what I'm doing wrong?
Many thanks.</p>
<h1>Events:</h1>
<pre><code>8s          Normal   Scheduled              pod/fixit-repair-catalog-job-pzcb5                Successfully assigned default/fixit-repair-catalog-job-pzcb5 to aks-default-80269438-vmss000000
8s          Normal   Pulled                 pod/fixit-repair-catalog-job-pzcb5                Container image &quot;node:14&quot; already present on machine
8s          Normal   Created                pod/fixit-repair-catalog-job-pzcb5                Created container fixit-repair-catalog-job
8s          Normal   Started                pod/fixit-repair-catalog-job-pzcb5                Started container fixit-repair-catalog-job
8s          Normal   SuccessfulCreate       job/fixit-repair-catalog-job                      Created pod: fixit-repair-catalog-job-pzcb5
4s          Normal   Completed              job/fixit-repair-catalog-job                      Job completed
</code></pre>
<h1>.Values:</h1>
<pre><code>replicaCount: 1
global:
  namespace: default
image:
  repository: node
  tag: 14

# filePath: .
filePath: &quot;/files&quot;
service:
  name: fixit-repair-catalog
  type: ClusterIP
  port: 3000


fullnameOverride: &quot;&quot;
</code></pre>
<h1>Job:</h1>
<pre><code>apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Release.Name }}-job
  labels:
    app: {{ .Release.Name }}
spec:
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: {{ .Release.Name }}-job
          image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
          command: [&quot;node&quot;]
          args: [ {{ .Files.Get &quot;/files/sendRepairData.js&quot; }} ]
          volumeMounts:
            - name: repair-catalog
              mountPath: /app/files
      volumes:
        - name: repair-catalog
          hostPath:
            path: {{ .Values.filePath }}
</code></pre>
<h1>sendRepairData.js:</h1>
<pre><code>const fs = require('fs');
const http = require('http');

// Function to send repair data
var savedRepairs = [];
function sendRepairData() {
  // Read the catalog JSON file
  try {
    const catalogData = fs.readFileSync('catalog.json'); 
    const catalog = JSON.parse(catalogData);

    // Access the repairs array from the catalog
    const repairs = catalog.repairs;

    // Process the repairs data as needed
    for (const repair of repairs) {
      const options = {
        host: 'server-clusterip-service', 
        port: 3000,
        path: 'api/server/repairs',
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          AuthToken: '', 
          apikey: ''
        },
      };

      const req = http.request(options, (res) =&gt; {
        let data = '';

        res.on('data', (chunk) =&gt; {
          data += chunk;
        });

        res.on('end', () =&gt; {
          console.log('Response:', data);
          // save response data for resetting the db in case of error
          savedRepairs.push(JSON.parse(data));

        });
      });

      req.on('error', (error) =&gt; {
        console.error('Error:', error.message);
      });

      req.write(JSON.stringify(repair));
      req.end();
    }
  } catch (error) {
    console.error('Error reading catalog file:', error.message);
    throw error;
  }
}


// function to reset the db
function deleteRepairs() {
  savedRepairs.forEach((repair) =&gt; {
    const options = {
    host: 'server-clusterip-service', 
    port: 3000, 
    path: `api/server/repairs/${repair.id}`,
    method: 'Delete',
    headers: {
      'Content-Type': 'application/json',
      AuthToken: '',  by your server
      apikey: ''
    },
  };

  const req = http.request(options, (res) =&gt; {
    let data = '';

    res.on('data', (chunk) =&gt; {
      data += chunk;
    });

    res.on('end', () =&gt; {
      console.log('Response:', data);
    });
  });

  req.on('error', (error) =&gt; {
    console.error('Error:', error.message);
  });
  req.end();
  });
  
}

function uploadRepairCatalog() {
  try {
    sendRepairData();
  } catch (error){
    deleteRepairs();
    throw error;
  }
}
// uploadRepairCatalog();

console.log('cron job done');
</code></pre>

Vincenzo
<p>In Kubernetes, you can't really run an unmodified base image and inject your application through mounts.  I see this as an occasional Docker &quot;pattern&quot; to avoid the one-line installation of Node locally, but in Kubernetes you can't reliably access the host system.  To use Kubernetes effectively, you all but must create a custom image and push it to some registry.</p>
<p>In particular, this block of YAML</p>
<pre class="lang-yaml prettyprint-override"><code>      volumes:
        - name: repair-catalog
          hostPath:
            path: {{ .Values.filePath }}
</code></pre>
<p>mounts an arbitrary directory <em>from whichever Node the Pod is running on</em>.  It cannot copy content from your local machine; indeed, Kubernetes doesn't track from where a Job might have been submitted, and can't connect back out of the cluster.  In most uses you should avoid <code>hostPath:</code> mounts.</p>
<p>Instead you need to create a custom image.  If you're just trying to run this script and it doesn't have any dependencies, the Dockerfile is pretty straightfoward</p>
<pre class="lang-bash prettyprint-override"><code>FROM node:14
WORKDIR /app
COPY sendRepairData.js ./
CMD [&quot;node&quot;, &quot;./sendRepairData.js&quot;]
</code></pre>
<p>You then need to either publish the image or get the image into the cluster somehow.  The generic answer, assuming some external image repository exists, looks like</p>
<pre class="lang-bash prettyprint-override"><code>docker build -t registry.example.com/send-repair-data:20230526 .
docker push registry.example.com/send-repair-data:20230526
</code></pre>
<p>In local environments you may be able to get away without the &quot;push&quot; command.  Minikube for example has a way to <a href="https://minikube.sigs.k8s.io/docs/handbook/pushing/#1-pushing-directly-to-the-in-cluster-docker-daemon-docker-env" rel="nofollow noreferrer">directly use an embedded Docker daemon</a> so long as you <code>eval $(minikube docker-env)</code> before building; Kind has a documented path to <a href="https://kind.sigs.k8s.io/docs/user/local-registry/" rel="nofollow noreferrer">run a local registry</a>.</p>
<p>You need to change your <code>values.yaml</code> to point at the image you just built</p>
<pre class="lang-yaml prettyprint-override"><code>image:
  repository: registry.example.com/send-repair-data
  tag: &quot;20230526&quot;
</code></pre>
<p>Then in the Job YAML, you don't need to provide the file content or override the command at all, all of these details are already embedded in the image.</p>
<pre class="lang-yaml prettyprint-override"><code>spec:
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: send-repair-data  # this name does not need to be configurable
          image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
      # the end
</code></pre>
