diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 12a1cdbfa..aed1b4e87 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -29,8 +29,14 @@ jobs: with: go-version-file: go.mod - - name: Lint + - name: Lint go code uses: golangci/golangci-lint-action@v6 with: args: --timeout=30m version: v1.60 + + # Run Spectral + - name: Lint OpenAPI spec + uses: stoplightio/spectral-action@2ad0b9302e32a77c1caccf474a9b2191a8060d83 # v0.8.11 + with: + file_glob: 'api/*.yaml' diff --git a/.spectral.yaml b/.spectral.yaml new file mode 100644 index 000000000..d47c47d31 --- /dev/null +++ b/.spectral.yaml @@ -0,0 +1 @@ +extends: ["spectral:oas"] diff --git a/Makefile b/Makefile index 5ffdfd0fc..7eee8f411 100644 --- a/Makefile +++ b/Makefile @@ -60,9 +60,13 @@ licensecheck: exit 1; \ fi +# Requires to install spectral. See https://github.com/stoplightio/spectral +oas-lint: + spectral lint api/*.yaml + # Run all the linters .PHONY: lint -lint: licensecheck go-lint +lint: licensecheck go-lint oas-lint # generates CRD using controller-gen .PHONY: crd diff --git a/api/webhook.yaml b/api/webhook.yaml new file mode 100644 index 000000000..c0c4f16aa --- /dev/null +++ b/api/webhook.yaml @@ -0,0 +1,265 @@ +--- +openapi: "3.0.0" +info: + version: v0.15.0 + title: External DNS Webhook Server + description: >- + Implements the external DNS webhook endpoints. + contact: + url: https://github.com/kubernetes-sigs/external-dns + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +tags: + - name: initialization + description: Endpoints for initial negotiation. + - name: listing + description: Endpoints to get listings of DNS records. + - name: update + description: Endpoints to update DNS records. +servers: + - url: http://localhost:8888 + description: Server url for a Kubernetes deployment. +paths: + /: + get: + summary: >- + Initialisation and negotiates headers and returns domain + filter. + description: | + Initialisation and negotiates headers and returns domain + filter. + operationId: negotiate + tags: [initialization] + responses: + '200': + description: | + The list of domains this DNS provider serves. + content: + application/external.dns.webhook+json;version=1: + schema: + $ref: '#/components/schemas/filters' + example: + filters: + - example.com + '500': + description: | + Negociation failed. + + /records: + get: + summary: Returns the current records. + description: | + Get the current records from the DNS provider and return them. + operationId: getRecords + tags: [listing] + responses: + '200': + description: | + Provided the list of DNS records successfully. + content: + application/external.dns.webhook+json;version=1: + schema: + $ref: '#/components/schemas/endpoints' + example: + - dnsName: "test.example.com" + recordTTL: 10 + recordType: 'A' + targets: + - "1.2.3.4" + '500': + description: | + Failed to provide the list of DNS records. + + post: + summary: Applies the changes. + description: | + Set the records in the DNS provider based on those supplied here. + operationId: setRecords + tags: [update] + requestBody: + description: | + This is the list of changes that need to be applied. There are + four lists of endpoints. The `create` and `delete` lists are lists + of records to create and delete respectively. The `updateOld` and + `updateNew` lists are paired. For each entry there's the old version + of the record and a new version of the record. + required: true + content: + application/external.dns.webhook+json;version=1: + schema: + $ref: '#/components/schemas/changes' + example: + create: + - dnsName: "test.example.com" + recordTTL: 10 + recordType: 'A' + responses: + '204': + description: | + Changes were accepted. + '500': + description: | + Changes were not accepted. + + /adjustendpoints: + post: + summary: Executes the AdjustEndpoints method. + description: | + Adjusts the records in the provider based on those supplied here. + operationId: adjustRecords + tags: [update] + requestBody: + description: | + This is the list of changes to be applied. + required: true + content: + application/external.dns.webhook+json;version=1: + schema: + $ref: '#/components/schemas/endpoints' + example: + - dnsName: "test.example.com" + recordTTL: 10 + recordType: 'A' + targets: + - "1.2.3.4" + responses: + '200': + description: | + Adjustments were accepted. + content: + application/external.dns.webhook+json;version=1: + schema: + $ref: '#/components/schemas/endpoints' + example: + - dnsName: "test.example.com" + recordTTL: 0 + recordType: 'A' + targets: + - "1.2.3.4" + '500': + description: | + Adjustments were not accepted. + +components: + schemas: + filters: + description: | + external-dns will only create DNS records for host names (specified in ingress objects and services with the external-dns annotation) related to zones that match filters. They can set in external-dns deployment manifest. + type: object + properties: + filters: + type: array + items: + type: string + example: "foo.example.com" + example: + - ".example.com" + example: + filters: + - ".example.com" + - ".example.org" + + endpoints: + description: | + This is a list of DNS records. + type: array + items: + $ref: '#/components/schemas/endpoint' + example: + - dnsName: foo.example.com + recordType: A + recordTTL: 60 + + endpoint: + description: | + This is a DNS record. + type: object + properties: + dnsName: + type: string + example: "foo.example.org" + targets: + $ref: '#/components/schemas/targets' + recordType: + type: string + example: "CNAME" + setIdentifier: + type: string + example: "v1" + recordTTL: + type: integer + format: int64 + example: 60 + labels: + type: object + additionalProperties: + type: string + example: "foo" + example: + foo: bar + providerSpecific: + type: array + items: + $ref: '#/components/schemas/providerSpecificProperty' + example: + - name: foo + value: bar + example: + dnsName: foo.example.com + recordType: A + recordTTL: 60 + + targets: + description: | + This is the list of targets that this DNS record points to. + So for an A record it will be a list of IP addresses. + type: array + items: + type: string + example: "::1" + example: + - "1.2.3.4" + - "test.example.org" + + providerSpecificProperty: + description: | + Allows provider to pass property specific to their implementation. + type: object + properties: + name: + type: string + example: foo + value: + type: string + example: bar + example: + name: foo + value: bar + + changes: + description: | + This is the list of changes send by `external-dns` that need to + be applied. There are four lists of endpoints. The `create` + and `delete` lists are lists of records to create and delete + respectively. The `updateOld` and `updateNew` lists are paired. + For each entry there's the old version of the record and a new + version of the record. + type: object + properties: + create: + $ref: '#/components/schemas/endpoints' + updateOld: + $ref: '#/components/schemas/endpoints' + updateNew: + $ref: '#/components/schemas/endpoints' + delete: + $ref: '#/components/schemas/endpoints' + example: + create: + - dnsName: foo.example.com + recordType: A + recordTTL: 60 + delete: + - dnsName: foo.example.org + recordType: CNAME diff --git a/docs/tutorials/webhook-provider.md b/docs/tutorials/webhook-provider.md index e3e52f06b..f3115506b 100644 --- a/docs/tutorials/webhook-provider.md +++ b/docs/tutorials/webhook-provider.md @@ -26,6 +26,8 @@ The following table represents the methods to implement mapped to their HTTP met | AdjustEndpoints | POST | /adjustendpoints | Provider specific adjustments of records | | ApplyChanges | POST | /records | Apply record | +OpenAPI spec is [here](../../api/webhook.yaml). + ExternalDNS will also make requests to the `/` endpoint for negotiation and for deserialization of the `DomainFilter`. The server needs to respond to those requests by reading the `Accept` header and responding with a corresponding `Content-Type` header specifying the supported media type format and version.