diff --git a/README.md b/README.md index 61bba67ec..0413ae6d0 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,13 @@ curl -H "Accept: application/vnd.goss-rspecish" localhost:8080/healthz ### Manually editing Goss files -Goss files can be manually edited to use: +Goss files can be manually edited to improve readability and expresssiveneess of tests. + +A [Json draft 7 schema](https://github.com/json-schema-org/json-schema-spec/blob/draft-07/schema.json) available in [docs/goss-json-schema.yaml](./docs/goss-json-schema.yaml) makes it easier to edit simple goss.yaml files in IDEs, providing usual coding assistance such as inline documentation, completion and static analysis. + +For example, to configure the Json schema in JetBrains intellij IDEA, follow [documented instructions](https://www.jetbrains.com/help/idea/json.html#ws_json_schema_add_custom), with arguments such as `schema url=https://raw.githubusercontent.com/goss-org/goss/master/docs/goss-json-schema.yaml`, `schema version=Json schema version 7`, `file path pattern=*/goss.yaml` + +In addition, Goss files can also be further manually edited (without full json support) to use: * [Patterns](https://github.com/goss-org/goss/blob/master/docs/manual.md#patterns) * [Advanced Matchers](https://github.com/goss-org/goss/blob/master/docs/manual.md#advanced-matchers) diff --git a/docs/goss-json-schema.yaml b/docs/goss-json-schema.yaml new file mode 100644 index 000000000..eda057cbb --- /dev/null +++ b/docs/goss-json-schema.yaml @@ -0,0 +1,804 @@ +$id: "https://github.com/goss-org/goss/master/docs/goss-json-schema.yaml" +# Note: this schema was authored using intellij support for Json schema version 7 +# providing coding assistance +# This schema is based on content from https://github.com/goss-org/goss/blob/master/docs/manual.md and +# and https://github.com/goss-org/goss/blob/master/README.md +# It was tested in intellij against https://github.com/goss-org/goss/master/docs/goss.yaml +# both for completion and code analysis use-cases. +# Limitations / missing support +# - patterns +# - advanced matchers +# - templates +$schema: http://json-schema.org/draft-07/schema# +title: "Goss-file-schema" +definitions: + title: + type: string + default: "UID must be between 50-100, GID doesn't matter. home is flexible" + description: title attribute is persisted when adding other resources with goss add + meta: + description: meta (arbitrary data) attributes are persisted when adding other resources with goss add + type: object + commandTest: + type: object + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + exit-status: + type: integer + description: "Validates the exit-status and output of a command" + exec: + description: "command to execute, defaults to the hash key" + type: string + stdout: + type: array + description: "can be a string or pattern, see https://github.com/goss-org/goss/blob/master/docs/manual.md#patterns" + items: + type: string + stderr: + type: array + description: "can be a string or pattern, see https://github.com/goss-org/goss/blob/master/docs/manual.md#patterns" + items: + type: string + timeout: + type: integer + description: "in milliseconds" + skip: + type: boolean + default: false + required: + - exit-status + addrTest: + required: + - reachable + - timeout + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + reachable: + type: boolean + default: true + timeout: + type: integer + default: 500 + examples: + - 500 + # optional attributes + local-address: + type: string + default: 127.0.0.1 + dnsTest: + required: + - resolvable + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + resolvable: + type: boolean + default: true + # optional attributes + addrs: + description: | + list of addresses e.g. ["127.0.0.1", "::1"] + type: array + items: + type: string + default: ["127.0.0.1", "::1"] + server: + description: "Eg 8.8.8.8. Also supports server:port " + type: string + default: 8.8.8.8 + timeout: + type: integer + default: 500 + description: in milliseconds (Only used when server attribute is provided) + fileTest: + required: + - exists + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + exists: + type: boolean + default: true + mode: + type: string + default: "0644" + size: + type: integer + default: 2118 + description: in bytes + owner: + type: string + default: root + group: + type: string + default: root + filetype: + type: string + default: file + enum: + - file + - symlink + - directory + contains: + type: array + description: Check file content for these patterns. can be a string or a pattern + items: + type: string + description: string or patterns + md5: + type: string + default: 7c9bb14b3bf178e82c00c2a4398c93cd + description: md5 checksum of file + + sha256: + type: string + default: 7f78ce27859049f725936f7b52c6e25d774012947d915e7b394402cfceb70c4c + description: "A stronger checksum alternatives to md5 (recommended)" + sha512: + type: string + default: cb71b1940dc879a3688bd502846bff6316dd537bbe917484964fe0f098e9245d80958258dc3bd6297bf42d5bd978cbe2c03d077d4ed45b2b1ed9cd831ceb1bd0 + description: "A stronger checksum alternatives to md5 (recommended)" + linked-to: + type: string + default: /usr/sbin/sendmail.sendmail + skip: + type: boolean + default: false + gossfileTest: + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + file: + type: string + default: myapp_gossfile.yaml + skip: + type: boolean + default: false + groupTest: + required: + - exists + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + exists: + type: boolean + default: true + uid: + type: integer + default: 65534 + gid: + type: integer + default: 65534 + groups: + type: object + skip: + type: boolean + default: false + httpTest: + required: + - status + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + status: + type: integer + default: 200 + allow-insecure: + type: boolean + default: false + no-follow-redirects: + type: boolean + default: false + description: Setting this to true will NOT follow redirects + timeout: + type: integer + default: 1000 + request-headers: + description: | + Set request header values, e.g. [ "Content-Type: text/html" ] + type: array + items: + type: string + default: [ "Content-Type: text/html" ] + headers: + type: array + description: | + Check http response headers for these patterns (e.g. "Content-Type: text/html") + NOTE: only the first Host header will be used to set the Request.Host value if multiple are provided. + items: + type: string + default: [ ] + request-body: + type: object + default: '{"key": "value"}' + description: request body + body: + type: array + description: Check http response content for these patterns + username: + type: string + description: username for basic auth + default: "" + password: + type: string + description: password for basic auth + default: "" + ca-file: + type: string + default: "" + description: | + CA root certs pem file, ex: /etc/ssl/cert.pem + cert-file: + type: string + default: "" + description: | + certificate file to use for authentication (used with key-file) + key-file: + type: string + default: "" + description: | + private-key file to use for authentication (used with cert-file) + proxy: + type: string + default: "" + description: | + proxy server to proxy traffic through. Proxy can also be set with environment variables http_proxy. + skip: + type: boolean + default: false + method: + type: string + default: PUT + description: http method + enum: # See https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods + # Note: not yet clear whether goss supports all methods + - GET + - PUT + - HEAD + - POST + - DELETE + - PATCH + - CONNECT + - OPTIONS + - TRACE + interfaceTest: + required: + - exists + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + exists: + type: boolean + default: true + addrs: + type: array + items: + type: string + default: + - 172.17.0.2/16 + - fe80::42:acff:fe11:2/64 + mtu: + type: integer + default: 1500 + kernelParamTest: + description: | + To see the full list of current values, run sysctl -a. + required: + - value + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + value: + type: string + default: Linux + matchingTest: + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + content: + anyOf: + - type: string + default: "some string" + - type: array + default: + - 2 + - type: object + default: + foo: bar + baz: ring + matches: + anyOf: + - type: integer + - type: object + - type: array + + mountTest: + required: + - exists + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + exists: + type: boolean + default: true + opts: + type: array + items: + type: string + default: + - rw + - relatime + source: + type: string + default: /dev/mapper/fedora-home + filesystem: + type: string + default: xfs + usage: + description: | + % of blocks used in this mountpoint + type: object + properties: + lt: + type: integer + default: 95 + packageTest: + required: + - installed + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + installed: + type: boolean + default: true + versions: + oneOf: + - type: object # matcher + - type: array + items: + type: string + default: + - 2.2.15 + skip: + type: boolean + default: false + portTest: + required: + - listening + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + listening: + type: boolean + default: true + ip: + description: what IP(s) is it listening on + type: array + items: + type: string + default: + - 0.0.0.0 + skip: + type: boolean + default: false + serviceTest: + required: + - enabled + - running + - skip + properties: + title: { "$ref":"#/definitions/title" } + meta: { "$ref":"#/definitions/meta" } + enabled: + type: boolean + default: true + running: + type: boolean + default: true + skip: + type: boolean + default: false + userTest: + required: + - exists + properties: + title: { "$ref": "#/definitions/title" } + meta: { "$ref": "#/definitions/meta" } + exists: + type: boolean + default: true + # optional attributes + uid: + anyOf: + - { "$ref": "#/definitions/matchingTest" } + - type: integer + default: 65534 + + gid: + anyOf: + - { "$ref": "#/definitions/matchingTest" } + - type: integer + default: 65534 + groups: + anyOf: + - { "$ref": "#/definitions/matchingTest" } + - type: array + items: + type: string + default: + - nfsnobody + home: + anyOf: + - { "$ref": "#/definitions/matchingTest" } + - type: string + default: /var/lib/nfs + shell: + type: string + default: /sbin/nologin + skip: + type: boolean + default: false + +description: "A file describing a series of tests" +type: "object" + +properties: + process: + type: object + description: "test executing a command" + additionalProperties: + $ref: "#/definitions/commandTest" + + addr: + type: object + description: "Validates if a remote address:port are accessible." + examples: + - tcp://ip-address-or-domain-name:80: + reachable: true + timeout: 500 + local-address: 127.0.0.1 + additionalProperties: + $ref: "#/definitions/addrTest" + + dns: + type: object + description: "Validates that the provided address is resolvable and the addrs it resolves to." + x-intellij-html-description: | +
Validates that the provided address is resolvable and the addrs it resolves to.
+dns:
+ localhost:
+ # required attributes
+ resolvable: true
+ # optional attributes
+ addrs:
+ - 127.0.0.1
+ - ::1
+ server: 8.8.8.8 # Also supports server:port
+ timeout: 500 # in milliseconds (Only used when server attribute is provided)
It is possible to validate the following types of DNS records, but requires the server
attribute be set:
To validate specific DNS address types, prepend the hostname with the type and a colon, a few examples:
+dns:
+ # Validate a CNAME record
+ CNAME:c.dnstest.io:
+ resolvable: true
+ server: 208.67.222.222
+ addrs:
+ - "a.dnstest.io."
+
+ # Validate a PTR record
+ PTR:8.8.8.8:
+ resolvable: true
+ server: 8.8.8.8
+ addrs:
+ - "dns.google."
+
+ # Validate and SRV record
+ SRV:_https._tcp.dnstest.io:
+ resolvable: true
+ server: 208.67.222.222
+ addrs:
+ - "0 5 443 a.dnstest.io."
+ - "10 10 443 b.dnstest.io."
Please note that if you want localhost
to only resolve 127.0.0.1
you'll need to use Advanced Matchers
dns:
+ localhost:
+ resolvable: true
+ addrs:
+ consist-of: [127.0.0.1]
+ timeout: 500 # in milliseconds
+ additionalProperties:
+ $ref: "#/definitions/dnsTest"
+
+ file:
+ type: object
+ description: "Validates the state of a file, directory, or symbolic link"
+ additionalProperties:
+ $ref: "#/definitions/fileTest"
+
+ gossfile:
+ type: object
+ description: |
+ Import other gossfiles from this one. This is the best way to maintain a large number of tests, and/or create profiles. See render for more examples. Glob patterns can be also be used to specify matching gossfiles.
+ You can specify the gossfile(s) either as the resource key, or using the 'file' attribute.
+
+ If the 'skip' attribute is true, then the file is not processed. If the filename is a glob pattern, then none of the matching files are processed. Note that this is not the same as skipping the contained resources; any overrides in the referenced gossfile will not be processed, and the resource count will not be incremented. Skipping a gossfile include is the same as omitting the gossfile resource entirely.
+ x-intellij-html-description: |
+ Import other gossfiles from this one. This is the best way to maintain a large number of tests, and/or create profiles. See render for more examples. Glob patterns can be also be used to specify matching gossfiles.
+ gossfile:
+ myapplication:
+ file: myapp_gossfile.yaml
+ skip: false
+ *.yaml:
+ skip: true
+ goss_httpd.yaml: {}
+ /etc/goss.d/*.yaml: {}
+
+
+ You can specify the gossfile(s) either as the resource key, or using the 'file' attribute.
+ If the 'skip' attribute is true, then the file is not processed. If the filename is a glob pattern, then none of the matching files are processed. Note that this is not the same as skipping the contained resources; any overrides in the referenced gossfile will not be processed, and the resource count will not be incremented. Skipping a gossfile include is the same as omitting the gossfile resource entirely.
+ additionalProperties:
+ $ref: "#/definitions/gossfileTest"
+
+ group:
+ type: object
+ description: "Validates the state of a group"
+ additionalProperties:
+ $ref: "#/definitions/groupTest"
+
+ http:
+ type: object
+ description: "description: Validates network interface values"
+ additionalProperties:
+ $ref: "#/definitions/httpTest"
+
+ interface:
+ type: object
+ description: "test "
+ additionalProperties:
+ $ref: "#/definitions/interfaceTest"
+
+ kernel-param:
+ type: object
+ description: "test "
+ additionalProperties:
+ $ref: "#/definitions/kernelParamTest"
+
+ matching:
+ type: object
+ description: "Validates specified content against a matcher. Best used with Templates."
+ x-intellij-html-description: |
+ Validates specified content against a matcher. Best used with Templates.
+ Templates:
+ With Let's say we have a data.json
file that gets generated as part of some testing pipeline:
+ {
+ "instance_count": 14,
+ "failures": 3,
+ "status": "FAIL"
+ }
+
+
+
+
+
+ This could then be passed into goss: goss --vars data.json validate
+ And then validated against:
+ matching:
+ check_instance_count: # Make sure there is at least one instance
+ content: {{ .Vars.instance_count }}
+ matches:
+ gt: 0
+
+ check_failure_count_from_all_instance: # expect no failures
+ content: {{ .Vars.failures }}
+ matches: 0
+
+ check_status:
+ content: {{ .Vars.status }}
+ matches:
+ - not: FAIL
+
+
+ Templates:
+ Without matching:
+ has_substr: # friendly test name
+ content: some string
+ matches:
+ match-regexp: some str
+ has_2:
+ content:
+ - 2
+ matches:
+ contain-element: 2
+ has_foo_bar_and_baz:
+ content:
+ foo: bar
+ baz: bing
+ matches:
+ and:
+ - have-key-with-value:
+ foo: bar
+ - have-key: baz
+
+
+
+ additionalProperties:
+ $ref: "#/definitions/matchTest"
+
+ mount:
+ type: object
+ description: "Validates mount point attributes."
+ additionalProperties:
+ $ref: "#/definitions/mountTest"
+
+ package:
+ type: object
+ description: |
+ Validates the state of a package"
+ NOTE: this check uses the --package parameter passed on the command line.
+ additionalProperties:
+ $ref: "#/definitions/packageTest"
+
+ port:
+ type: object
+ description: |
+ Validates the state of a local port.
+
+ Note: Goss might consider your port to be listening on tcp6 rather than tcp, try running goss add port .. to see how goss detects it. (explanation)
+ x-intellij-html-description: |
+ Validates the state of a local port.
+ Note: Goss might consider your port to be listening on tcp6
rather than tcp
, try running goss add port ..
to see how goss detects it. (explanation)
+ port:
+ # {tcp,tcp6,udp,udp6}:port_num
+ tcp:22:
+ # required attributes
+ listening: true
+ # optional attributes
+ ip: # what IP(s) is it listening on
+ - 0.0.0.0
+ skip: false
+ additionalProperties:
+ $ref: "#/definitions/portTest"
+
+ service:
+ type: object
+ description: "Validates the state of a service."
+ additionalProperties:
+ $ref: "#/definitions/serviceTest"
+
+ user:
+ type: object
+ description: |
+ Validates the state of a user"
+ NOTE: This check is inspecting the contents of local passwd file /etc/passwd, this does not validate remote users (e.g. LDAP).
+ additionalProperties:
+ $ref: "#/definitions/userTest"
+
+
diff --git a/docs/goss.yaml b/docs/goss.yaml
new file mode 100644
index 000000000..f420cc413
--- /dev/null
+++ b/docs/goss.yaml
@@ -0,0 +1,213 @@
+# This sample goss.yaml file is used to test the json schema goss-json-schema.yaml
+
+process:
+ mysql:
+ exec: "mysql -h"
+ exit-status: 0
+
+
+addr:
+ tcp:
+ reachable: true
+ timeout: 500
+ local-address: 127.0.0.1
+
+dns:
+ localhost:
+ addrs:
+ - ::1
+ resolvable: true
+
+file:
+ /ect/password:
+ exists: true
+ mode: "0644"
+ size: 2118
+ owner: root
+ group: root
+ filetype: file
+ contains:
+ - hrll
+ md5: 7c9bb14b3bf178e82c00c2a4398c93cd
+ sha256: 7f78ce27859049f725936f7b52c6e25d774012947d915e7b394402cfceb70c4c
+ sha512: cb71b1940dc879a3688bd502846bff6316dd537bbe917484964fe0f098e9245d80958258dc3bd6297bf42d5bd978cbe2c03d077d4ed45b2b1ed9cd831ceb1bd0
+ linked-to: /usr/sbin/sendmail.sendmail
+ skip: false
+
+
+gossfile:
+ myapplication:
+ file: myapp_gossfile.yaml
+ skip: false
+
+group:
+ nfsnobody:
+ exists: true
+ skip: false
+ gid: 65534
+ nobody:
+ exists: true
+
+
+http:
+ https://www.google.com:
+ status: 200
+ allow-insecure: false
+ no-follow-redirects: false
+ timeout: 1000
+ username: ""
+ password: ""
+ ca-file: ""
+ cert-file: ""
+ key-file: ""
+ proxy: ""
+ skip: false
+ method: GET
+
+interface:
+ eth0:
+ exists: true
+ addrs:
+ - " 1"
+ mtu: 1500
+
+kernel-param:
+ kernel.ostype:
+ value: Linux
+
+matching:
+ check_instance_count:
+ content: {{ .Vars.instance_count }}
+ matches:
+ gt: 0
+ check_failure_count_from_all_instance:
+ content: {{ .Vars.failures }}
+ matches: 0
+ check_status:
+ content: {{ .Vars.status }}
+ matches:
+ - not: FAIL
+
+ has_substr: # friendly test name
+ content: some string
+ matches:
+ match-regexp: some str
+ has_2:
+ content:
+ - 2
+ matches:
+ contain-element: 2
+ has_foo_bar_and_baz:
+ content:
+ foo: bar
+ baz: bing
+ matches:
+ and:
+ - have-key-with-value:
+ foo: bar
+ - have-key: baz
+
+# TODO: sprig syntax fails to accept upper block below
+# https://github.com/goss-org/goss/blob/master/docs/manual.md#examples-2
+# sping_basic:
+# content: { { "hello!" | upper | repeat 5 }}
+# matches:
+# match-regexp: "HELLO!HELLO!HELLO!HELLO!HELLO!"
+
+mount:
+ /home:
+ exists: true
+ opts:
+ - rw
+ source: /dev/mapper/fedora-home
+ filesystem: xfs
+ usage:
+ lt: 95
+
+package:
+ httpd:
+ installed: true
+ versions:
+ - "2.1"
+ skip: false
+# https://github.com/goss-org/goss/blob/master/README.md#manually-editing-goss-files
+ kernel:
+ installed: true
+ versions:
+ and:
+ - have-len: 3
+ - not:
+ contain-element: "4.1.0"
+port:
+ tcp:22:
+ listening: true
+ ip:
+ - "1"
+ skip: false
+
+service:
+ sshd:
+ enabled: true
+ skip: false
+ running: true
+
+user:
+ nfsbody:
+ exists: true
+ uid: 65534
+ gid: 65534
+ groups:
+ - nfsnobody
+ home: /var/lib/nfs
+ shell: /sbin/nologin
+ skip: false
+
+ nobody:
+ exists: true
+ uid:
+ lt: 500
+ groups:
+ consist-of: [nobody]
+
+ sshd:
+ title: UID must be between 50-100, GID doesn't matter. home is flexible
+ meta:
+ desc: Ensure sshd is enabled and running since it's needed for system management
+ sev: 5
+ exists: true
+ uid:
+ # Validate that UID is between 50 and 100
+ and:
+ gt: 50
+ lt: 100
+ home:
+ # Home can be any of the following
+ or:
+ - /var/empty/sshd
+ - /var/run/sshd
+
+# https://github.com/goss-org/goss/blob/master/README.md#manually-editing-goss-files
+
+## Matchers
+
+# https://github.com/goss-org/goss/blob/master/docs/manual.md#examples-1
+# TODO: not yet clear whether this is a valid example or should instead be in a matching block
+#example:
+# content:
+# - 1.0.1
+# - 1.9.9
+# matches:
+# semver-constraint: ">1.0.0 <2.0.0 !=1.5.0"
+
+## Templates
+
+# TODO: templates are not valid yaml hence can't be supporter by Json schema
+#file:
+# {{- range mkSlice "/etc/passwd" "/etc/group"}}
+# {{.}}:
+#exists: true
+#mode: "0644"
+#owner: root
+#group: root
+#filetype: file
+# {{end}}