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.

+

With Templates:

+

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
+ +
+

Without Templates:

+
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}}