Is the Ansible Inventory compatible with OpenAPI standards? - json

I´m trying to specify the API used by ansibles dynamic inventory.
Does anyone have experience with solving the incompatibility?
For a single host it might work as following:
The Ansible json output will be like this:
{
"ansible_host": "172.16.19.123",
"proxy": "somehost.domain.fake"
}
The openapi.yml
paths:
/api/inventory/host/:
get:
summary: Gets One Ansible Host
parameters:
- in: query
name: hostname
schema:
type: string
required: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SingleHost'
components:
schemas:
SingleHost: # A Single Host for ansible-inventory --host [hostname] requests
type: object
properties:
ansible_host:
type: string
description: IP-Address of the host
ansible_port:
type: integer
description: ssh-port number of host
But when specifiyng the endpoint for ansible-inventory --list it gets tricky already..
example of a ansible inventory yml
{
"HostGroupName-A": {
"hosts": [
"Host-A"
]
},
"HostGroupName-B": {
"hosts": [
"Host-A",
"Host-B"
]
}
}
Should I just avoid using openapi to specify this?

Related

How can I pass Rundeck variables to a JSON file?

I have a JSON with key pairs and I want to access the values from Rundeck Options dynamically during the job execution.
For shell script, we can do a $RD_OPTIONS_<>.
Similarly is there some format I can use in a JSON file?
Just use #option.myoption# in a inline-script step.
You need a tool to use on an inline script step to manipulate JSON files on Rundeck. I made an example using JQ. Alternatively, you can use bash script-fu to reach the same goal.
For example, using this JSON file:
{
"books": [{
"fear_of_the_dark": {
"author": "John Doe",
"genre": "Mistery"
}
}]
}
Update the file with the following jq call:
To test directly in your terminal
jq '.books[].fear_of_the_dark += { "ISBN" : "9999" }' myjson.json
On Rundeck Inline-script
echo "$(jq ''.books[].fear_of_the_dark += { "ISBN" : "#option.isbn#" }'' myjson.json)" > myjson.json
Check how looks on an inline-script job (check here to know how to import the job definition to your Rundeck instance).
- defaultTab: nodes
description: ''
executionEnabled: true
id: d8f1c0e7-a7c6-43d4-91d9-25331cc06560
loglevel: INFO
name: JQTest
nodeFilterEditable: false
options:
- label: isbn number
name: isbn
required: true
plugins:
ExecutionLifecycle: null
scheduleEnabled: true
sequence:
commands:
- description: original file content
exec: cat myjson.json
- description: pass the option and save the content to the json file
fileExtension: .sh
interpreterArgsQuoted: false
script: 'echo "$(jq ''.books[].fear_of_the_dark += { "ISBN" : "#option.isbn#"
}'' myjson.json)" > myjson.json'
scriptInterpreter: /bin/bash
- description: modified file content (after jq)
exec: cat myjson.json
keepgoing: false
strategy: node-first
uuid: d8f1c0e7-a7c6-43d4-91d9-25331cc06560
Finally, check the result.
Here you can check more about executing scripts on Rundeck and here more about the JQ tool.

Ansible access JSON object with dynamic key and special character

I have an Ansible script that performs API call and stores the JSON result in a variable. The JSON result from the API call is in the following format:
{
"result": {
"Object::18": {
"message": "object message"
},
message: "hello"
}
}
In this case the key Object::18 is dynamically generated from the ID given as argument in the Ansible script:
$ ansible-playbook test.yaml --extra-var='id=18'
How do I get the JSON value for key result.Object::{{id}}.message in this case? Please find below a sample Ansible script:
- name: Get API object
hosts: localhost
vars:
object_id: '{{ id }}'
# Sample JSON result returned by the API call
# Object::<id> (the key "Object::<id>" depends on the ID given as argument for the ansible script)
# For example, if run using --extra-vars "id=18", it will be Object::18. If id=19 then Object::19
sample_json_result:
{
"result": {
"Object::18": {
"message": "object message"
},
message: "hello"
}
}
tasks:
- debug:
# How to get result.Object::<id>.message???
# For example 1: --extra-vars "id=18", result.Object::18.message
# Example 2: --extra-vars "id=19", result.Object::19.message
msg: '{{ sample_json_result.result }}'
# The following does NOT work
#msg: '{{ sample_json_result.result.Object\:\:{{id}}.message }}'
Thank you and please let me know if you have any further questions.
EDIT:
For those who are interested, solution as suggested by Zeitounator
- name: Get API object
hosts: localhost
vars:
object_id: '{{ id }}'
# Sample JSON result returned by the API call
# Object::<id> (the key "Object::<id>" depends on the ID given as argument for the ansible script)
# For example, if run using --extra-vars "id=18", it will be Object::18. If id=19 then Object::19
sample_json_result:
{
"result": {
"Object::18": {
"message": "object message"
},
message: "hello"
}
}
tasks:
- debug:
msg: '{{ sample_json_result.result["Object::" ~ id].message }}'

Problem passing parameters created in Terraform to CloudFormation

Using guidance from a previous SO post: [https://stackoverflow.com/questions/43266506/is-it-possible-to-execute-a-cloudformation-file-in-terraform]:
I am trying to pass two parameters created in my Terraform template into a Cloudformation template as parameters:
My terraform (v.0.13.5) code:
resource "aws_cloudformation_stack" "cloudwatch-synthetics-canary" {
provider = aws.primary
name = "cloudwatch-synthetics"
parameters = {
CloudWatchSyntheticsRole = aws_iam_role.cloudwatch_synthetics_role.arn,
ResultsBucket = aws_s3_bucket.results_bucket.arn
}
template_body = file("${path.module}/cloudwatch_canary.yml")
}
resource "aws_iam_role" "cloudwatch_synthetics_role" {
provider = aws.primary
name = "CloudWatchSyntheticsRole"
description = "Allows Cloudwatch Lambda to operate in this account."
assume_role_policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowLambdaAssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
POLICY
}
resource "aws_iam_policy" "cloudwatch_synthetics_role_policy" {
provider = aws.primary
name = "CloudWatchSyntheticsRolePolicy"
path = "/"
description = "Addtional allowances for the synthetics role"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
# --- SNIPPED FOR BREVITY ---
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "cloudwatch_synthetics_role_policy_attachment" {
provider = aws.primary
role = aws_iam_role.cloudwatch_synthetics_role.name
policy_arn = aws_iam_policy.cloudwatch_synthetics_role_policy.arn
}
My Cloudformation code:
Parameters:
CanaryName:
Type: String
Default: my-canary
MaxLength: 21
HostName:
Type: String
Default: api.myhost.net
MaxLength: 128
Path:
Type: String
Default: /v1/status
MaxLength: 256
Port:
Type: Number
Default: 443
CloudWatchSyntheticsRole:
Type: AWS::IAM::Role
ResultsBucket:
Type: AWS::S3::Bucket
...
Resources:
ExecutionRoleArn:
Fn::GetAtt:
- CloudWatchSyntheticsRole
- Arn # <-- TRIED WITH AND WITHOUT THIS
In Terraform.io, the error is as follows:
Error: Creating CloudFormation stack failed: ValidationError: Template error: instance of Fn::GetAtt references undefined resource CloudWatchSyntheticsRole
status code: 400, request id: 694c...
I tried making the types in the CFT as Strings, but that didn't seem to work, either. The Fn::GetAtt: also had '- Arn' below the CloudwatchSyntheticsRole to refer to the Arn, but since I'm passing that in directly, I tried removing it.
I feel like I'm close, but missing something and just need some help from some fresh eyes.
It looks like the solution, found by a co-worker of mine is pretty simple:
Make sure ALL CFT variables are in Terraform and pass any default values through Terraform as literals to Cloudformation
Remove all default parameters from CloudFormation, but still keep the Name and Type of variable at a minimum. Other constraints like 'MaxLength' are ok, too.
Example:
Terraform.tf:
resource "aws_cloudformation_stack" "cloudwatch-synthetics-canary" {
provider = aws.primary
name = "cloudwatch-synthetics"
parameters = {
CanaryName = "my-canary",
HostName = "api.server.net",
Path = "/v1/status",
Port = 443,
RoleArn = aws_iam_role.cloudwatch_synthetics_role.arn,
S3Location = "s3://${aws_s3_bucket.results_bucket.id}"
}
template_body = file("${path.module}/cloudwatch_canary.yml")
}
Cloudformation.yml:
Parameters:
CanaryName:
Type: String
MaxLength: 21
HostName:
Type: String
MaxLength: 128
Path:
Type: String
MaxLength: 256
Port:
Type: Number
RoleArn:
Type: String
S3Location:
Type: String
MaxLength: 1024

OpenApi required property in nested objects not working

I need to describe an api having in request body an object with required fields and one of these fields it's an object itself having another set of required fields.
I'm using open api v3 and swagger editor (https://editor.swagger.io/)
After i put my .yaml file onto the editor I generate an html client (> generate client > html). Then I open the static page index.html generated in the .zip file obtaning this schema:
Table of Contents
body
secureoauthservicesv2Nested_nestedobj
body
id
Integer id of nested obj
nestedobj
secureoauthservicesv2Nested_nestedobj
secureoauthservicesv2Nested_nestedobj
nested object
field1 (optional)
String
field2 (optional)
String
I expect field1 to be required and field2 to be optional but it's not.
This is my .yaml file
openapi: 3.0.0
info:
title: Example API
description: Example API specification
version: 0.0.1
servers:
- url: https://example/api
paths:
/secure/oauth/services/v2/Nested:
post:
summary: Try nested
description: Used to post Nested obj
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- id
- nestedobj
properties:
id:
type: integer
description: id of nested obj
nestedobj:
type: object
required:
- field1
description: nested object
properties:
field1:
type: string
field2:
type: string
responses:
'200':
description: Nested object OK
Solved!
I used components and schemas, but I think this could be a bug, opened an issue on swagger editor repo:
https://github.com/swagger-api/swagger-editor/issues/1952
openapi: 3.0.0
info:
title: Example API
description: Example API specification
version: 0.0.2
servers:
- url: https://example/api
paths:
/secure/oauth/services/v2/Nested:
post:
summary: Try nested
description: Used to post Nested obj
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- id
- nestedobj
properties:
id:
type: integer
description: id of nested obj
nestedobj:
$ref: '#/components/schemas/nestedobj'
responses:
'200':
description: Nested object OK
components:
schemas:
element:
type: object
required:
- fieldArray1
properties:
fieldArray1:
type: string
description: field array
fieldArray2:
type: number
nestedobj:
type: object
required:
- field1
description: nested object
properties:
field1:
$ref: '#/components/schemas/woah'
field2:
type: string
woah:
type: object
required:
- woahthis
description: woah this
properties:
field3:
type: array
items:
$ref: '#/components/schemas/element'
woahthis:
type: number
description: numeber woah this
EDIT 23/08/21:
I opened a bug in swagger-codegen github in april 2019 but it still has no response whatsoever

Swagger POST Json Body Parameter Schema YAML

i'm working on a RESTful API using swagger-api and swagger-editor for routes.
I can't figure out why the JSON i am sending through body, never reaches my controller.
here is my YAML
schemes:
- http
- https
produces: [application/json, multipart/form-data, application/x-www-form-urlencoded]
paths:
/projects:
x-swagger-router-controller: project
post:
description: create a new project
operationId: postProjects
consumes:
- application/json
parameters:
- name: param1
in: body
description: description
required: false
schema:
$ref: "#/definitions/Project"
responses:
"200":
description: Success
schema:
$ref: "#/definitions/Project"
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
definitions:
Project:
properties:
name:
type: string
required:
- name
an example of the post request i'm sending.
curl -v -X POST -H "Content-Type: application/json" -d '{"name":"test"}' http://127.0.0.1:10010/projects
and the response
{"message":"Request validation failed: Parameter (param1) failed schema validation","code":"SCHEMA_VALIDATION_FAILED","failedValidation":true,"results":{"errors":[{"code":"OBJECT_MISSING_REQUIRED_PROPERTY","message":"Missing required property: name","path":[]}],"warnings":[]},"path":["paths","/projects","post","parameters","0"],"paramName":"param1"}
If i set the parameter "name" as not required, i just received an empty response like this
{ param1:
{ path: [ 'paths', '/projects', 'post', 'parameters', '0' ],
schema:
{ name: 'param1',
in: 'body',
description: 'description',
required: false,
schema: [Object] },
originalValue: {},
value: {} } }
I have no clue why since other format such as header, path or formdata works fine.
I always receive an empty object. req.swagger.params has no value.
I tried several schema but even the simplest is not working.
i can tell from the header that 'content-type': 'application/json'.
So the content type is set, the schema validates a simple string argument named "name". Everything should be ok. but still not.
This issue has been fixed.
It wasn't swagger related.
I built an API with nodeJs and i realized i didn't have a middleware to handle body parameter.
So because i missed a step before enabled the swagger middleware, i couldn't do anything with body parameter.
The main reason behind getting null values when sending json data to the API backend is the paramater path most of the time you give and the naming you give the parameter.
You also have to declare explicitly the type of schema you expect
You have to set parameter name to body and set in: body so that it picks the body object as a JSON
An example is here. You can try it out
/auth/register:
post:
tags:
- Auth
parameters:
- in: body
name: user
description: Create a new user.
schema:
type: object
required:
- firstName
- lastName
- email
- password
- confirmPassword
properties:
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
confirmPassword:
type: string
example:
firstName: Jane
lastName: Doe
email: janedoe#gmail.com
password: pass
confirmPassword: pass
responses:
"200":
description: OK