Getting Status code 422 when creating Kubernetes Job using nodejs - javascript

I am creating a Kubernetes Job within NodeJS class After importing the library #kubernetes/client-node, I created an object to use the module BatchV1Api inside the function which I am exporting to other class in which I have defined the body of the Kubernetes job like this:
//listJobs.js
import { post } from '../kubeClient.js';
const kubeRoute = async (ctx) => {
const newJob = {
metadata: {
name: 'countdown',
},
spec: {
template: {
metadata: {
name: 'countdown',
},
},
spec: {
containers: [
{
name: 'counter',
image: 'centos:7',
command: 'bin/bash, -c, for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done',
}],
restartPolicy: 'Never',
},
},
};
const kubeClient = post();
kubeClient.createNamespacedJob('default', newJob);
ctx.body = {
// listConfigMap: (await kubeClient.listConfigMapForAllNamespaces()).body,
listJobs: (await kubeClient.listJobForAllNamespaces()).body,
// listService: (await kubeClient.listServiceForAllNamespaces()).body,
};
};
export default kubeRoute;
Then I created a router class to request the post method like:
import post from './listJobs.js';
const apiRouter = new Router();
apiRouter.post('/api/v1/newJob', post);
when executing the application and requesting the route localhost:3000/api/v1/newJob as a post request in postman, it is showing status code 422 (with some very long output, as in the screenshot) in the vs code terminal and some Kubernetes information in postman body, but it is not creating any job or pod.
Does anyone have any idea, why there is 422 code at the end?

Status code 422 Unprocessable Entity means that server understand the content type, and the syntax of the request is correct, but it was unable to process the contained instructions.
In your case though, the Job manifest looks off.
I'm not an expert in JavaScript kubernetes client, but newJob body looks weird. The resulting yaml should look like this
apiVersion: batch/v1
kind: Job
metadata:
name: countdown
spec:
template:
spec:
containers:
- name: counter
image: centos:7
command: 'bin/bash, -c, for i in {9..1} ; do echo $i ; done' #fixed this one for you
restartPolicy: Never
In your case the second spec is a child of spec. It should be a child of template, so:
{
"metadata": {
"name": "countdown"
},
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "counter",
"image": "centos:7",
"command": "bin/bash, -c, for i in {9..1} ; do echo $i ; done"
}
],
"restartPolicy": "Never"
}
}
}
}

Related

How to add permissions to a new API with STRAPI (not from dashboard)

I need to do a new api in order to send an email with sendgrid. I followed the official doc and other examples so I did:
config/plugins
module.exports = ({ env }) => ({
email: {
provider: 'sendgrid',
providerOptions: {
apiKey: env('SENDGRID_API_KEY'),
},
settings: {
defaultFrom: 'juliasedefdjian#strapi.io',
defaultReplyTo: 'juliasedefdjian#strapi.io',
},
},
});
then I did a new folder named email in api folder
api/email/config/routes.json
{
"routes": [
{
"method": "POST",
"path": "/email",
"handler": "email.index",
"config": {
"policies": []
}
}
]
}
finally under api/email/controllers/email.js
const { default: createStrapi } = require('strapi');
module.exports = {
index: async (ctx) => {
//build email with data from ctx.request.body
await createStrapi.plugins['email'].services.email.send({
to: 'email#email.com',
from: 'email#email.com',
replyTo: 'email#email.com',
subject: 'test',
text: 'test',
});
ctx.send('Email sent!');
},
};
The real problem is that /email api returns me a 403 even if I did this from the dashboard:
I have done many APIs with strapi but I have never sent emails with it.
Is there a way to add permissions from the code? I have to say that if I use GET method it works, but I need to do it with a POST method, which doesn't. Did I miss something?

Yeoman generator add a new file generated exsiting project

I've yeoman generator which generate a simple sproject successfully.
I want that after the project generation, in latter time that the use will have the ability to generate a new file deployment.yaml under the app folder, however it needs to read some data from the main generator
for example appName as the sub-generator needs to generate a new file
inside the generated application.
e.g. yo tdk
This command generates a new project
And when I run yo tdk:event (or something similar) it will generate a new file inside the project app folder
For illustration I've created this very simple generator
const Generator = require("yeoman-generator");
module.exports = class extends Generator {
prompting() {
this.props = {
appName: "my-app",
srvName: "my-service"
};
const prompts = [
{
name: "appName",
message: "Project name: ",
type: "input",
default: this.props.appName
},
{
name: "srvName",
message: "Service name: ",
type: "input",
default: this.props.srvName
}
];
return this.prompt(prompts).then(props => {
this.props = props;
});
}
writing() {
this.fs.copyTpl(
this.templatePath("app"),
this.destinationPath(this.props.appName),
this.props
);
}
};
This generator have two simple question
app name
service name
And it will generate a project like
myapp /root
-app /folder
- service.yaml /single file at the project generation
The generated service.yaml looks like following:
apiVersion: v1
kind: Service
metadata:
name: <%= appName %>
spec:
selector:
app: <%= srvName %>
ports:
- protocol: TCP
port: 80
Now after the generation of the project with this service.yaml file
I want in latter time (after the project generation)to add new file deployment.yaml under the app folder
deployment.yaml
apiVersion: v1
kind: Deployment
metadata:
name: <%= appName %> //this is the appname from the project generation
spec:
replicas: <%= replica %>
selector:
app: <%= srvName %>
The appName & srvName are coming from the main generator,
(I saw that there is option to share data between sub generator
https://yeoman.io/authoring/storage.html , not sure how to share this between generators )
and the replica should come from the new/sub generator
This is the project structure after the generation
myapp /root
-app /folder
- service.yaml /single file at the project generation
- deployment.yaml / new file added to the project under app folder
Like user start another generator/sub and have a new question e.g. how much replicas do you want? and then generates the file.
How can I do it ?
update
This is my project strucutre
myapp
- node_modules
- package.json //here I declare the main-generator command -> tdk
- generators
-- app
---index.ts
--deployment
---index.ts
---package.json //here I declare the sub-generator command -> deploy
- node_modules
- package.json
-.yo-rc.json //here I see the data that I keep via config.set api
Update
When I call to the sub generator via program like
const yeoman = require('yeoman-environment');
const env = yeoman.createEnv();
env.lookup(function () {
env.run("tdk:deploy", {
replicas: 100
}, (err) => {
console.log("done", err);
});
});
I got error:
out from config undefined : undefined //the undefind is from the console in the sub-generator
done TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined
at validateString (internal/validators.js:125:11)
at Object.join (path.js:1037:7)
I put a console.log in the subgenerator code like
initializing() {
this.srvName = this.config.get("srvName");
this.appName = this.config.get("appName");
console.log("out from config", this.srvName, ":", this.appName);
}
And when I run the subgenerator I got empty config ( from the .yo-rc.json)
while checking the .yo-rc.json . I was able to see the entry from the main generator, the data was stored but when I run it from the program it doesnt find it...any idea ?
This is the link for both project (very basic yeoman generator which demonstrate the point) just need to run npm install for both projects
and for the generator run also npm link.
At the end: a project should be generated with two files
1. service.yaml // generated from the main generator
2. deployment.yaml - // generated from sub generator with the properties from the main & sub generator
currently, the deployment.yaml file is not generated
https://drive.google.com/drive/folders/1kBnZxpVcRR9qhGZagVtod7W4wFmt73C6
1 . generator-tdk - Generator and sub-generator
2. yeomanEnv - The code which is running the sub-generator to create the file inside the generated project
What am I doing wrong ? :(
if there is a way from the sub-generator to read the .yo-rc.json , it can help
You can set the values to config inside configuring of the main generator like this:
configuring() {
this.config.set('appName', this.props.appName);
this.config.set('srvName', this.props.srvName);
}
and read the values inside the sub-generators:
initializing() {
this.srvName = this.config.get("srvName");
this.appName = this.config.get("appName");
}
So you'll have access to these values via this.srvName and this.appName upon writing.
Example code:
app/index.js:
const Generator = require("yeoman-generator");
module.exports = class extends Generator {
prompting() {
this.props = {
appName: "my-app",
srvName: "my-service",
};
const prompts = [
{
name: "appName",
message: "Project name: ",
type: "input",
default: this.props.appName,
},
{
name: "srvName",
message: "Service name: ",
type: "input",
default: this.props.srvName,
},
];
return this.prompt(prompts).then((props) => {
this.props = props;
});
}
configuring() {
this.config.set('appName', this.props.appName);
this.config.set('srvName', this.props.srvName);
}
writing() {
this.fs.copyTpl(
this.templatePath("app"),
this.destinationPath(this.props.appName),
this.props
);
}
};
deploy/index.js:
const Generator = require("yeoman-generator");
module.exports = class extends Generator {
initializing() {
this.srvName = this.config.get("srvName");
this.appName = this.config.get("appName");
}
prompting() {
this.props = {
replicas: 0,
};
const prompts = [
{
name: "replica",
message: "how much replicas do you want?",
type: "input",
default: this.props.replicas,
},
];
return this.prompt(prompts).then((props) => {
this.props = props;
});
}
writing() {
this.fs.copyTpl(
this.templatePath("deploy"),
this.destinationPath(this.appName),
{
srvName: this.srvName,
appName: this.appName,
...this.props,
}
);
}
};
and commands:
yo <name for the main project generation
yo <name>:deploy to ask for replicas and create deployment.yaml
To execute the sub-generator without the use of yo:
var yeoman = require("yeoman-environment");
var env = yeoman.createEnv();
env.lookup(function () {
env.run("<name>:deploy", {
replicas: 100
}, (err) => {
console.log("done", err);
});
});
and a sample sub-generator that skips question if values are passed via options (deploy/index.js):
const Generator = require("yeoman-generator");
module.exports = class extends Generator {
initializing() {
this.srvName = this.config.get("srvName");
this.appName = this.config.get("appName");
}
prompting() {
this.props = {
replicas: 0,
};
const prompts = [
{
name: "replicas",
message: "which app to generate?",
type: "input",
default: this.props.replicas,
when: !this.options.replicas, // disable the question if it's found in options
},
];
return this.prompt(prompts).then((props) => {
this.props = props;
// set values from options (if found)
this.props.replicas = this.options.replicas || this.props.replicas;
});
}
writing() {
this.fs.copyTpl(
this.templatePath("deploy"),
this.destinationPath(this.appName),
{
srvName: this.srvName,
appName: this.appName,
...this.props,
}
);
}
};

gatsby-source-stripe develop errors

i want to gatsby stripe serverless checkout , i added products on stripe. but i cant install gatsby-source-stripe or gatsby-source-stripe-products. error;
error UNHANDLED EXCEPTION
Error: /...../node_modules/gatsby-source-stripe/gatsby-node.js:4
exports.sourceNodes = async ({ actions }, { objects = [], secretKey = "" }) => {
^^^^^^^^^^^^
SyntaxError: Invalid shorthand property initializer
gatsby-config
{
resolve: `gatsby-source-stripe`,
options: {
objects: [
'balance',
'customers',
'products',
'applicationFees',
'skus',
'subscriptions'
],
secretKey: process.env.STRIPE_SECRET
}
},
i have .env file.
Make sure you have entered test SKUs in Stripe - you can do so by toggling the View test data.

Ember get filtered data from the store/mirage

I am trying to build an app for smarthome devices with ember. I already have installed mirage and declare an array which is called data. It holds arrays like households, users and devices. Now I am stuck with get filtered data from the store and i am really confused by the behaviour of the store. For this reason I already read some equal topics like this Filtering store data in ember but this doesn´t work in my case.
Actually this is my router.js
Router.map(function() {
this.route('about');
this.route('users', function() {
});
this.route('households', function() {
this.route('index', { path: '/:user_id' })
this.route('rooms',{ path: '/:household_id' });
this.route('devices');
});
});
If I am going to households.index I want to see all households which have the user-id in his member-array. The following code snipped shows an example of my data-structure.
"users": [
{
"id":101,
"forename":"Peter",
"surname":"Peterson",
"memberIn":[
501
]
},
{
"id":102,
"forename":"Anna",
"surname":"Peterson",
"memberIn":[
501
]
}
]
"households":[
{
"id":501,
"name":"Zuhause",
"admin":131000,
"member":[
101,
102
]
}
I am calling the route household.index like this {{#link-to "households.index" user.id}} and my route in household.index looks like this
model(params) {
//holt alle Haushalte und gibt dann nur die Haushalte weiter, die auch den aktuellen Benutzer als Member haben.
return this.get('store').findAll('household').then(results => results.filter((site) => {
return site.get('member').filter(x => x == params.user_id).length > 0;
}));
}
And my config.js part at mirage like this:
this.get('/households', function(db, request) {
return { households: data.households };
});
This works fine!
But in my opinion the server is responsible for giving me the data I am requesting for. So all I want is that I send a specific request and just get the households that I need.
My attempt:
index.js:
export default Route.extend({
model(params) {
return this.get('store').find('household', params.user_id);
}
});
config js part:
this.get('/households/:id', function(db, request) {
console.log('household get');
console.log(data.households.filter(x => x.member.filter(x => x == request.params.id).length > 0));
return { households: data.households.filter(x => x.member.filter(x => x == request.params.id).length > 0) };
});
Debug Error:
Error while processing route: households.index payload.data is null
I cant understand why this error occurs. The log gives me the array i want to return.
Make sure you are using RestSerializer in mirage,
// mirage/serializers/application.js
import { RestSerializer } from 'ember-cli-mirage';
export default RestSerializer;
and for the right format refer RESTAdapterAPI.
Just ensure are you returning data in the below format,
for the single response,
{
"posts": {
"id": 1,
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz"
}
}
and for more than one,
{
"posts": [
{
"id": 1,
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz"
},
{
"id": 2,
"title": "Rails is omakase",
"author": "D2H"
}
]
}
By default Ember Data ships with the JSONAPISerializer and this assumes a few things about the way your server (in this case mirage) is formatting data. In this case the document that mirage is returning is expected to generally conform to the JSON API Spec and have a top-level member data where the response's primary data resides.
If you're looking to stick to your custom formatted responses you may want to check out the Ember Data's JSONSerializer. Otherwise, returning the following should get you closer:
return { data: db.households.filter(x => x.member.filter(x => x == request.params.id).length > 0) };
or you could leverage the JSONAPISerializer that ships with Mirage
See more here Ember Guides | Serializers

Error serving HTML files from an Azure function

I am trying to open, read and return an HTML files using Azure functions. I am developing locally and the logs says that the function executed successfully however on the browser I am getting 500 internal server error. Am I doing something wrong in here?
const fs = require('fs');
const path = require('path');
const mime = require('../node_modules/mime-types');
module.exports = function (context, req) {
const staticFilesFolder = 'www/build/';
const defaultPage = 'index.html';
getFile(context, req.query.file);
function getFile(context, file) {
const homeLocation = process.env["HOME"];
if(!file || file == null || file === undefined){
context.done(null,{status:200,body:"<h1>Define a file</h1>",headers:{
"Content-Type":" text/html; charset=utf-8"
}});
}
fs.readFile(path.resolve(path.join(homeLocation, staticFilesFolder, file)),
(err, htmlContent) => {
if (err) {
getFile(context, "404.html");
}
else {
const res = {
status: 200,
body: htmlContent,
headers:{
"Content-Type": mime.lookup(path.join(homeLocation, staticFilesFolder, file))
}
}
context.done(null,res);
}
})
}
};
Note
I am sure that 404.html exists and index.html exists. When I log the contents of htmlContent it is giving the correct output.
functions.json
{
"disabled": false,
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"methods":["get"],
"route":"home",
"name": "req"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
Response on Chrome
If I removed "Content-Length" header the status code changes to 406.
Update 1 The code seems to be running normally on Azure Portal but it is not working when running it locally.
It looks like you are combining two methods of returning data from an http triggered function(context.res and context.done()): https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node#accessing-the-request-and-response
Since you are using context.res, try removing context.done();
You are making an incorrect use of context.res, you shouldn't be overwriting it but instead leveraging the methods provided by the Response class provided in the Azure NodeJS worker. If you are using using VSCode you'll get intellisense for these methods. Otherwise see: https://github.com/Azure/azure-functions-nodejs-worker/blob/dev/src/http/Response.ts
Your code should look something like this instead.
context.res.setHeader('content-type', 'text/html; charset=utf-8')
context.res.raw(htmlContent)
Using context.res.raw or context.res.send will already perform the context.done call for you.
Make sure you use content-type=text/html; charset-utf8 instead of content-type=text/html or you'll trigger an issue with the returned content-type. Instead of returning content-type=text/html you end up getting content-type=text/plain which will fail to render your html.
Addressed on: https://github.com/Azure/azure-webjobs-sdk-script/issues/2053

Categories