From 50e55b0b607f5ac62c69745d9c70fb9b2d6a500b Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Mon, 14 Nov 2022 23:40:22 -0500 Subject: [PATCH] generate CRD docs from spec (#428) --- .github/workflows/docs.yaml | 32 +++ .pre-commit-config.yaml | 2 +- Makefile | 5 + README.md | 35 ++-- apis/ingress/v1/pomerium_types.go | 182 ++++++++++++------ .../bases/ingress.pomerium.io_pomerium.yaml | 161 +++++++++++----- config/crd/files.go | 9 + deployment.yaml | 161 +++++++++++----- docs/cmd/main.go | 56 ++++++ docs/docs.go | 2 + docs/known_formats.go | 18 ++ docs/parse.go | 154 +++++++++++++++ docs/template.go | 18 ++ docs/templates/header.tmpl | 17 ++ docs/templates/object-properties.tmpl | 38 ++++ docs/templates/object.tmpl | 4 + docs/templates/objects.tmpl | 6 + go.mod | 2 +- 18 files changed, 713 insertions(+), 189 deletions(-) create mode 100644 .github/workflows/docs.yaml create mode 100644 config/crd/files.go create mode 100644 docs/cmd/main.go create mode 100644 docs/docs.go create mode 100644 docs/known_formats.go create mode 100644 docs/parse.go create mode 100644 docs/template.go create mode 100644 docs/templates/header.tmpl create mode 100644 docs/templates/object-properties.tmpl create mode 100644 docs/templates/object.tmpl create mode 100644 docs/templates/objects.tmpl diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 00000000..d21e75f3 --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,32 @@ +name: Docs +on: + push: + branches: + - main + +jobs: + pull-request: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f + with: + go-version: 1.19.x + + - name: generate docs + run: make docs + + - name: Create pull request in the documentations repo + uses: paygoc6/action-pull-request-another-repo@v1.0.5 + env: + API_TOKEN_GITHUB: ${{ secrets.APPARITOR_GITHUB_TOKEN }} + with: + source_folder: "reference.md" + destination_repo: "pomerium/documentation" + destination_folder: "content/docs/k8s" + destination_base_branch: "main" + destination_head_branch: update-k8s-reference-${{ github.sha }} + user_email: "dmishin@pomerium.com" + user_name: "wasaga" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d3fbae8d..d0c5d9ed 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: hooks: - id: trailing-whitespace - id: end-of-file-fixer - exclude: "(docs/.*|integration/tpl/files/.*)" + exclude: "(integration/tpl/files/.*)" - id: check-yaml exclude: "deployment.yaml" - id: check-added-large-files diff --git a/Makefile b/Makefile index 63656950..91f448e0 100644 --- a/Makefile +++ b/Makefile @@ -208,6 +208,11 @@ deployment: @echo "==> $@" @$(KUSTOMIZE) build config/default > deployment.yaml +.PHONY: docs +docs: manifests + @echo "==> $@" + @go run docs/cmd/main.go > reference.md + # # --- internal development targets # diff --git a/README.md b/README.md index 9f1811ae..d1d091e0 100644 --- a/README.md +++ b/README.md @@ -9,40 +9,27 @@ See [docs for usage details](https://www.pomerium.com/docs/k8s/ingress) for end- # Installation -``` -kubectl apply -f https://raw.githubusercontent.com/pomerium/ingress-controller/main/deployment.yaml +See [Quick Start](https://www.pomerium.com/docs/k8s/quickstart) for a step-by-step guide. + +```shell +kubectl apply -f https://raw.githubusercontent.com/pomerium/ingress-controller/v0.19.0/deployment.yaml ``` -- `pomerium` namespace is created that would contain an installation. -- `pomerium.ingress.pomerium.io` cluster-scoped CRD is created. -- `pomerium` `IngressClass`. Assign that `IngressClass` to the `Ingress` objects that should be managed by Pomerium. +The manifests-based installation: + +- Creates `pomerium` namespace. +- Creates `pomerium.ingress.pomerium.io` cluster-scoped CRD. +- Creates `pomerium` `IngressClass`. Assign that `IngressClass` to the `Ingress` objects that should be managed by Pomerium. - All-in-one Pomerium deployment with a single replica is created. - Pomerium expects a `pomerium` CRD named `global` to be created. - A one time `Job` to generate `pomerium/bootstrap` secrets, that have to be referenced from the CRD via `secrets` parameter. -Pomerium requires further configuration to become operational. +Pomerium requires further configuration to become operational (see below). # Configuration Default Pomerium deployment is configured to watch `global` CRD. -That may be customized via command line arguments. -Most Pomerium configuration is set via CRD. - -```yaml -apiVersion: ingress.pomerium.io/v1 -kind: Pomerium -metadata: - name: global -spec: - authenticate: - url: https://authenticate.localhost.pomerium.io - certificates: - - pomerium/wildcard-localhost-pomerium-io - identityProvider: - provider: xxxxxxx - secret: pomerium/idp - secrets: pomerium/bootstrap -``` +[Pomerium should be configured via the CRD](https://www.pomerium.com/docs/k8s/reference). _Note:_: the configuration must be complete. i.e. if you're missing a referenced secret, it would not be accepted. diff --git a/apis/ingress/v1/pomerium_types.go b/apis/ingress/v1/pomerium_types.go index 087f206e..99ed9b33 100644 --- a/apis/ingress/v1/pomerium_types.go +++ b/apis/ingress/v1/pomerium_types.go @@ -25,37 +25,46 @@ import ( // Where available, Pomerium also supports pulling additional data (like groups) using directory synchronization. // An additional API token is required for directory sync. https://www.pomerium.com/docs/identity-providers/ type IdentityProvider struct { - // Provider one of accepted providers - see https://www.pomerium.com/reference/#identity-provider-name. + // Provider is the short-hand name of a built-in OpenID Connect (oidc) identity provider to be used for authentication. + // To use a generic provider, set to oidc. // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=auth0;azure;google;okta;onelogin;oidc;ping;github Provider string `json:"provider"` - // URL is identity provider url, see https://www.pomerium.com/reference/#identity-provider-url. + // URL is the base path to an identity provider's OpenID connect discovery document. + // See Identity Providers guides for details. // +kubebuilder:validation:Optional // +kubebuilder:validation:Type=string // +kubebuilder:validation:Format=uri // +kubebuilder:validation:Pattern=`^https://` URL *string `json:"url"` - // Secret containing IdP provider specific parameters - // and must contain at least client_id and client_secret values, - // an optional `service_account` field, mapped to https://www.pomerium.com/reference/#identity-provider-service-account + // Secret containing IdP provider specific parameters. + // and must contain at least client_id and client_secret values. // +kubebuilder:validation:Required // +kubebuilder:validation:Type=string // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:Format="namespace/name" Secret string `json:"secret"` - // ServiceAccountFromSecret is no longer supported, see https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync + // ServiceAccountFromSecret is no longer supported, + // see Upgrade Guide. // +optional ServiceAccountFromSecret *string `json:"serviceAccountFromSecret,omitempty" deprecated:"idp_directory_sync"` - // RequestParams see https://www.pomerium.com/reference/#identity-provider-request-params + // RequestParams to be added as part of a signin request using OAuth2 code flow. + // + // +kubebuilder:validation:Format="namespace/name" // +optional RequestParams map[string]string `json:"requestParams,omitempty"` // RequestParamsSecret is a reference to a secret for additional parameters you'd prefer not to provide in plaintext. + // +kubebuilder:validation:Format="namespace/name" // +optional RequestParamsSecret *string `json:"requestParamsSecret,omitempty"` - // Scopes see https://www.pomerium.com/reference/#identity-provider-scopes. + // Scopes Identity provider scopes correspond to access privilege scopes + // as defined in Section 3.3 of OAuth 2.0 RFC6749. // +optional Scopes []string `json:"scopes,omitempty"` - // RefreshDirectory is no longer supported, please see https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync + // RefreshDirectory is no longer supported, + // please see Upgrade Guide. + // // +optional RefreshDirectory *RefreshDirectorySettings `json:"refreshDirectory" deprecated:"idp_directory_sync"` } @@ -71,32 +80,29 @@ type RefreshDirectorySettings struct { } // RedisStorage defines REDIS databroker storage backend bootstrap parameters. -// Redis is supported for legacy deployment, and new deployments should use PostgreSQL. +// Redis is supported for legacy deployments, new deployments should use PostgreSQL. type RedisStorage struct { // Secret specifies a name of a Secret that must contain - // `connection` key. - // see https://www.pomerium.com/docs/reference/data-broker-storage-connection-string + // connection key. // +kubebuilder:validation:Required // +kubebuilder:validation:Type=string // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:Format="namespace/name" Secret string `json:"secret"` - // TLSSecret should refer to a k8s secret of type `kubernetes.io/tls` - // and allows to specify an optional databroker storage client certificate and key, see - // - https://www.pomerium.com/docs/reference/data-broker-storage-certificate-file - // - https://www.pomerium.com/docs/reference/data-broker-storage-certificate-key-file + // TLSSecret should refer to a k8s secret of type kubernetes.io/tls + // that would be used to perform TLS connection to REDIS. // +kubebuilder:validation:Optional // +kubebuilder:validation:Type=string // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:Format="namespace/name" TLSSecret *string `json:"tlsSecret"` - // CASecret should refer to a k8s secret with key `ca.crt` that must be a PEM-encoded - // certificate authority to use when connecting to the databroker storage engine - // see https://www.pomerium.com/docs/reference/data-broker-storage-certificate-authority - // + // CASecret should refer to a k8s secret with key ca.crt that must be a PEM-encoded + // certificate authority to use when connecting to the databroker storage engine. // +kubebuilder:validation:Optional // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Format="namespace/name" CASecret *string `json:"caSecret"` - // TLSSkipVerify disables TLS certificate chain validation - // see https://www.pomerium.com/docs/reference/data-broker-storage-tls-skip-verify + // TLSSkipVerify disables TLS certificate chain validation. // +kubebuilder:validation:Optional // +kubebuilder:validation:Type=boolean TLSSkipVerify bool `json:"tlsSkipVerify"` @@ -105,28 +111,33 @@ type RedisStorage struct { // PostgresStorage defines Postgres connection parameters. type PostgresStorage struct { // Secret specifies a name of a Secret that must contain - // `connection` key - // for the connection DSN format and parameters, see - // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING - // the following keywords are not allowed to be part of the parameters, - // as they must be populated via `tlsCecret` and `caSecret` fields + // connection key. See + // DSN Format and Parameters. + // Do not set sslrootcert, sslcert and sslkey via connection string, + // use tlsCecret and caSecret CRD options instead. // +kubebuilder:validation:Required // +kubebuilder:validation:Type=string // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:Format="namespace/name" Secret string `json:"secret"` - // TLSSecret should refer to a k8s secret of type `kubernetes.io/tls` + // TLSSecret should refer to a k8s secret of type kubernetes.io/tls // and allows to specify an optional client certificate and key, - // by constructing `sslcert` and `sslkey` connection string parameter values - // see https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS + // by constructing sslcert and sslkey connection string + // + // parameter values. + // // +kubebuilder:validation:Optional // +kubebuilder:validation:Type=string // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:Format="namespace/name" TLSSecret *string `json:"tlsSecret"` - // CASecret should refer to a k8s secret with key `ca.crt` containing CA certificate - // that, if specified, would be used to populate `sslrootcert` parameter of the connection string + // CASecret should refer to a k8s secret with key ca.crt containing CA certificate + // that, if specified, would be used to populate sslrootcert parameter of the connection string. + // // +kubebuilder:validation:Optional // +kubebuilder:validation:Type=string // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:Format="namespace/name" CASecret *string `json:"caSecret"` } @@ -147,15 +158,29 @@ type Storage struct { // Authenticate service configuration parameters type Authenticate struct { - // AuthenticateURL should be publicly accessible URL - // the non-authenticated persons would be referred to - // see https://www.pomerium.com/reference/#authenticate-service-url + // AuthenticateURL is a dedicated domain URL + // the non-authenticated persons would be referred to. + // + //

+ // // +kubebuilder:validation:Required // +kubebuilder:validation:Type=string // +kubebuilder:validation:Format=uri // +kubebuilder:validation:Pattern=`^https://` URL string `json:"url"` - // CallbackPath see https://www.pomerium.com/reference/#authenticate-callback-path + // CallbackPath sets the path at which the authenticate service receives callback responses + // from your identity provider. The value must exactly match one of the authorized redirect URIs for the OAuth 2.0 client. + // + //

This value is referred to as the redirect_url in the OpenIDConnect and OAuth2 specs.

+ //

Defaults to /oauth2/callback

+ // // +optional CallbackPath *string `json:"callbackPath,omitempty"` } @@ -163,67 +188,106 @@ type Authenticate struct { // Cookie customizes HTTP cookie set by Pomerium. // note that cookie_secret is part of the main configuration secret type Cookie struct { - // Name see https://docs.pomerium.com/docs/reference/cookie-name + // Name sets the Pomerium session cookie name. + // Defaults to _pomerium // +optional Name *string `json:"name,omitempty"` - // Domain see https://docs.pomerium.com/docs/reference/cookie-domain + // Domain defaults to the same host that set the cookie. + // If you specify the domain explicitly, then subdomains would also be included. // +optional Domain *string `json:"domain,omitempty"` - // Secure see https://docs.pomerium.com/docs/reference/cookie-secure + // Secure if set to false, would make a cookie accessible over insecure protocols (HTTP). + // Defaults to true. // +optional Secure *bool `json:"secure,omitempty"` - // HTTPOnly see https://docs.pomerium.com/docs/reference/cookie-http-only + // HTTPOnly if set to false, the cookie would be accessible from within the JavaScript. + // Defaults to true. // +optional HTTPOnly *bool `json:"httpOnly,omitempty"` - // Expire see https://docs.pomerium.com/docs/reference/cookie-expire + // Expire sets cookie and Pomerium session expiration time. + // Once session expires, users would have to re-login. + // If you change this parameter, existing sessions are not affected. + //

See Session Management + // (Enterprise) for a more fine-grained session controls.

+ //

Defaults to 14 hours.

+ // +kubebuilder:validation:Format=duration // +optional Expire *metav1.Duration `json:"expire,omitempty"` } -// PomeriumSpec defines the desired state of Settings +// PomeriumSpec defines Pomerium-specific configuration parameters. type PomeriumSpec struct { // Authenticate sets authenticate service parameters // +kubebuilder:validation:Required Authenticate Authenticate `json:"authenticate"` - // IdentityProvider see https://www.pomerium.com/docs/identity-providers/ + // IdentityProvider configure single-sign-on authentication and user identity details + // by integrating with your Identity Provider + // // +kubebuilder:validation:Required IdentityProvider IdentityProvider `json:"identityProvider"` // Certificates is a list of secrets of type TLS to use + // +kubebuilder:validation:Format="namespace/name" // +optional Certificates []string `json:"certificates"` - // Secrets references a Secret that must have the following keys - // - shared_secret - // - cookie_secret - // - signing_key + // Secrets references a Secret with Pomerium bootstrap parameters. + // + //

+ //

+ //

+ //

+ // In a default Pomerium installation manifest, they would be generated via a + // one-time job + // and stored in a pomerium/bootstrap Secret. + // You may re-run the job to rotate the secrets, or update the Secret values manually. + //

+ // // +kubebuilder:validation:Required // +kubebuilder:validation:Type=string // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:Format="namespace/name" Secrets string `json:"secrets"` - // Storage defines persistent storage for sessions and other data - // it will use in-memory if none specified - // see https://www.pomerium.com/docs/topics/data-storage + // Storage defines persistent storage for sessions and other data. + // See Storage for details. + // If no storage is specified, Pomerium would use a transient in-memory storage (not recommended for production). + // // +kubebuilder:validation:Optional Storage *Storage `json:"storage,omitempty"` - // Cookie defines Pomerium cookie options + // Cookie defines Pomerium session cookie options. // +optional Cookie *Cookie `json:"cookie,omitempty"` // JWTClaimHeaders convert claims from the assertion token - // into HTTP headers. We recommend you only use it for compatibility - // with legacy applications, and use JWT assertion header directly - // for new applications, read more at https://www.pomerium.com/docs/topics/getting-users-identity + // into HTTP headers and adds them into JWT assertion header. + // Please make sure to read + // + // Getting User Identity guide. + // // +optional JWTClaimHeaders map[string]string `json:"jwtClaimHeaders,omitempty"` } -// ResourceStatus represents the outcome of the latest attempt to reconcile it with Pomerium. +// ResourceStatus represents the outcome of the latest attempt to reconcile +// relevant Kubernetes resource with Pomerium. type ResourceStatus struct { - // ObservedGeneration represents the .metadata.generation that was last presented to Pomerium. + // ObservedGeneration represents the .metadata.generation that was last presented to Pomerium. ObservedGeneration int64 `json:"observedGeneration,omitempty"` // ObservedAt is when last reconciliation attempt was made. ObservedAt metav1.Time `json:"observedAt,omitempty"` @@ -232,16 +296,16 @@ type ResourceStatus struct { // Error that prevented latest observedGeneration to be synchronized with Pomerium. // +optional Error *string `json:"error"` - // Warnings + // Warnings while parsing the resource. // +optional Warnings []string `json:"warnings"` } -// PomeriumStatus defines the observed state of Settings +// PomeriumStatus represents configuration and Ingress status. type PomeriumStatus struct { // Routes provide per-Ingress status. Routes map[string]ResourceStatus `json:"ingress,omitempty"` - // settingsStatus represent most recent main configuration reconciliation status. + // SettingsStatus represent most recent main configuration reconciliation status. SettingsStatus *ResourceStatus `json:"settingsStatus,omitempty"` } diff --git a/config/crd/bases/ingress.pomerium.io_pomerium.yaml b/config/crd/bases/ingress.pomerium.io_pomerium.yaml index 3ea8806b..ed8d6b94 100644 --- a/config/crd/bases/ingress.pomerium.io_pomerium.yaml +++ b/config/crd/bases/ingress.pomerium.io_pomerium.yaml @@ -34,17 +34,29 @@ spec: metadata: type: object spec: - description: PomeriumSpec defines the desired state of Settings + description: PomeriumSpec defines Pomerium-specific configuration parameters. properties: authenticate: description: Authenticate sets authenticate service parameters properties: callbackPath: - description: CallbackPath see https://www.pomerium.com/reference/#authenticate-callback-path + description: "CallbackPath sets the path at which the authenticate + service receives callback responses from your identity provider. + The value must exactly match one of the authorized redirect + URIs for the OAuth 2.0 client. \n

This value is referred + to as the redirect_url in the OpenIDConnect and OAuth2 specs.

+

Defaults to /oauth2/callback

" type: string url: - description: AuthenticateURL should be publicly accessible URL - the non-authenticated persons would be referred to see https://www.pomerium.com/reference/#authenticate-service-url + description: "AuthenticateURL is a dedicated domain URL the non-authenticated + persons would be referred to. \n

" format: uri pattern: ^https:// type: string @@ -53,33 +65,50 @@ spec: type: object certificates: description: Certificates is a list of secrets of type TLS to use + format: namespace/name items: type: string type: array cookie: - description: Cookie defines Pomerium cookie options + description: Cookie defines Pomerium session cookie options. properties: domain: - description: Domain see https://docs.pomerium.com/docs/reference/cookie-domain + description: Domain defaults to the same host that set the cookie. + If you specify the domain explicitly, then subdomains would + also be included. type: string expire: - description: Expire see https://docs.pomerium.com/docs/reference/cookie-expire + description: Expire sets cookie and Pomerium session expiration + time. Once session expires, users would have to re-login. If + you change this parameter, existing sessions are not affected. +

See Session + Management (Enterprise) for a more fine-grained session + controls.

Defaults to 14 hours.

+ format: duration type: string httpOnly: - description: HTTPOnly see https://docs.pomerium.com/docs/reference/cookie-http-only + description: HTTPOnly if set to false, the cookie + would be accessible from within the JavaScript. Defaults to + true. type: boolean name: - description: Name see https://docs.pomerium.com/docs/reference/cookie-name + description: Name sets the Pomerium session cookie name. Defaults + to _pomerium type: string secure: - description: Secure see https://docs.pomerium.com/docs/reference/cookie-secure + description: Secure if set to false, would make a cookie accessible + over insecure protocols (HTTP). Defaults to true. type: boolean type: object identityProvider: - description: IdentityProvider see https://www.pomerium.com/docs/identity-providers/ + description: IdentityProvider configure single-sign-on authentication + and user identity details by integrating with your Identity + Provider properties: provider: - description: Provider one of accepted providers - see https://www.pomerium.com/reference/#identity-provider-name. + description: Provider is the short-hand name of a built-in OpenID + Connect (oidc) identity provider to be used for authentication. + To use a generic provider, set to oidc. enum: - auth0 - azure @@ -92,7 +121,8 @@ spec: type: string refreshDirectory: description: RefreshDirectory is no longer supported, please see - https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync + Upgrade + Guide. properties: interval: description: interval is the time that pomerium will sync @@ -110,29 +140,37 @@ spec: requestParams: additionalProperties: type: string - description: RequestParams see https://www.pomerium.com/reference/#identity-provider-request-params + description: RequestParams to be added as part of a signin request + using OAuth2 code flow. + format: namespace/name type: object requestParamsSecret: description: RequestParamsSecret is a reference to a secret for additional parameters you'd prefer not to provide in plaintext. + format: namespace/name type: string scopes: - description: Scopes see https://www.pomerium.com/reference/#identity-provider-scopes. + description: Scopes Identity provider scopes correspond to access + privilege scopes as defined in Section 3.3 of OAuth 2.0 RFC6749. items: type: string type: array secret: - description: Secret containing IdP provider specific parameters - and must contain at least client_id and client_secret values, - an optional `service_account` field, mapped to https://www.pomerium.com/reference/#identity-provider-service-account + description: Secret containing IdP provider specific parameters. + and must contain at least client_id and client_secret + values. + format: namespace/name minLength: 1 type: string serviceAccountFromSecret: description: ServiceAccountFromSecret is no longer supported, - see https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync + see Upgrade + Guide. type: string url: - description: URL is identity provider url, see https://www.pomerium.com/reference/#identity-provider-url. + description: URL is the base path to an identity provider's OpenID + connect discovery document. See Identity + Providers guides for details. format: uri pattern: ^https:// type: string @@ -144,18 +182,31 @@ spec: additionalProperties: type: string description: JWTClaimHeaders convert claims from the assertion token - into HTTP headers. We recommend you only use it for compatibility - with legacy applications, and use JWT assertion header directly - for new applications, read more at https://www.pomerium.com/docs/topics/getting-users-identity + into HTTP headers and adds them into JWT assertion header. Please + make sure to read + Getting User Identity guide. type: object secrets: - description: Secrets references a Secret that must have the following - keys - shared_secret - cookie_secret - signing_key + description: "Secrets references a Secret with Pomerium bootstrap + parameters. \n

In a default + Pomerium installation manifest, they would be generated via a one-time + job and stored in a pomerium/bootstrap Secret. + You may re-run the job to rotate the secrets, or update the Secret + values manually.

" + format: namespace/name minLength: 1 type: string storage: description: Storage defines persistent storage for sessions and other - data it will use in-memory if none specified see https://www.pomerium.com/docs/topics/data-storage + data. See Storage + for details. If no storage is specified, Pomerium would use a transient + in-memory storage (not recommended for production). properties: postgres: description: Postgres specifies PostgreSQL database connection @@ -163,25 +214,29 @@ spec: properties: caSecret: description: CASecret should refer to a k8s secret with key - `ca.crt` containing CA certificate that, if specified, would - be used to populate `sslrootcert` parameter of the connection - string + ca.crt containing CA certificate that, if specified, + would be used to populate sslrootcert parameter + of the connection string. + format: namespace/name minLength: 1 type: string secret: description: Secret specifies a name of a Secret that must - contain `connection` key for the connection DSN format and - parameters, see https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING - the following keywords are not allowed to be part of the - parameters, as they must be populated via `tlsCecret` and - `caSecret` fields + contain connection key. See DSN + Format and Parameters. Do not set sslrootcert, + sslcert and sslkey via connection + string, use tlsCecret and caSecret + CRD options instead. + format: namespace/name minLength: 1 type: string tlsSecret: description: TLSSecret should refer to a k8s secret of type - `kubernetes.io/tls` and allows to specify an optional client - certificate and key, by constructing `sslcert` and `sslkey` - connection string parameter values see https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS + kubernetes.io/tls and allows to specify an + optional client certificate and key, by constructing sslcert + and sslkey connection string + parameter values. + format: namespace/name minLength: 1 type: string required: @@ -192,25 +247,27 @@ spec: properties: caSecret: description: CASecret should refer to a k8s secret with key - `ca.crt` that must be a PEM-encoded certificate authority - to use when connecting to the databroker storage engine - see https://www.pomerium.com/docs/reference/data-broker-storage-certificate-authority + ca.crt that must be a PEM-encoded certificate + authority to use when connecting to the databroker storage + engine. + format: namespace/name type: string secret: description: Secret specifies a name of a Secret that must - contain `connection` key. see https://www.pomerium.com/docs/reference/data-broker-storage-connection-string + contain connection key. + format: namespace/name minLength: 1 type: string tlsSecret: description: TLSSecret should refer to a k8s secret of type - `kubernetes.io/tls` and allows to specify an optional databroker - storage client certificate and key, see - https://www.pomerium.com/docs/reference/data-broker-storage-certificate-file - - https://www.pomerium.com/docs/reference/data-broker-storage-certificate-key-file + kubernetes.io/tls that would be used to perform + TLS connection to REDIS. + format: namespace/name minLength: 1 type: string tlsSkipVerify: description: TLSSkipVerify disables TLS certificate chain - validation see https://www.pomerium.com/docs/reference/data-broker-storage-tls-skip-verify + validation. type: boolean required: - secret @@ -222,12 +279,12 @@ spec: - secrets type: object status: - description: PomeriumStatus defines the observed state of Settings + description: PomeriumStatus represents configuration and Ingress status. properties: ingress: additionalProperties: description: ResourceStatus represents the outcome of the latest - attempt to reconcile it with Pomerium. + attempt to reconcile relevant Kubernetes resource with Pomerium. properties: error: description: Error that prevented latest observedGeneration @@ -239,7 +296,7 @@ spec: format: date-time type: string observedGeneration: - description: ObservedGeneration represents the .metadata.generation + description: ObservedGeneration represents the .metadata.generation that was last presented to Pomerium. format: int64 type: integer @@ -248,7 +305,7 @@ spec: successfully synced with pomerium. type: boolean warnings: - description: Warnings + description: Warnings while parsing the resource. items: type: string type: array @@ -258,7 +315,7 @@ spec: description: Routes provide per-Ingress status. type: object settingsStatus: - description: settingsStatus represent most recent main configuration + description: SettingsStatus represent most recent main configuration reconciliation status. properties: error: @@ -271,7 +328,7 @@ spec: format: date-time type: string observedGeneration: - description: ObservedGeneration represents the .metadata.generation + description: ObservedGeneration represents the .metadata.generation that was last presented to Pomerium. format: int64 type: integer @@ -280,7 +337,7 @@ spec: successfully synced with pomerium. type: boolean warnings: - description: Warnings + description: Warnings while parsing the resource. items: type: string type: array diff --git a/config/crd/files.go b/config/crd/files.go new file mode 100644 index 00000000..94891591 --- /dev/null +++ b/config/crd/files.go @@ -0,0 +1,9 @@ +// Package crd embeds CRD spec +package crd + +import _ "embed" + +// SettingsCRD is Pomerium CRD Yaml +// +//go:embed bases/ingress.pomerium.io_pomerium.yaml +var SettingsCRD []byte diff --git a/deployment.yaml b/deployment.yaml index bba3a4cd..0ce9d480 100644 --- a/deployment.yaml +++ b/deployment.yaml @@ -42,17 +42,29 @@ spec: metadata: type: object spec: - description: PomeriumSpec defines the desired state of Settings + description: PomeriumSpec defines Pomerium-specific configuration parameters. properties: authenticate: description: Authenticate sets authenticate service parameters properties: callbackPath: - description: CallbackPath see https://www.pomerium.com/reference/#authenticate-callback-path + description: "CallbackPath sets the path at which the authenticate + service receives callback responses from your identity provider. + The value must exactly match one of the authorized redirect + URIs for the OAuth 2.0 client. \n

This value is referred + to as the redirect_url in the OpenIDConnect and OAuth2 specs.

+

Defaults to /oauth2/callback

" type: string url: - description: AuthenticateURL should be publicly accessible URL - the non-authenticated persons would be referred to see https://www.pomerium.com/reference/#authenticate-service-url + description: "AuthenticateURL is a dedicated domain URL the non-authenticated + persons would be referred to. \n

" format: uri pattern: ^https:// type: string @@ -61,33 +73,50 @@ spec: type: object certificates: description: Certificates is a list of secrets of type TLS to use + format: namespace/name items: type: string type: array cookie: - description: Cookie defines Pomerium cookie options + description: Cookie defines Pomerium session cookie options. properties: domain: - description: Domain see https://docs.pomerium.com/docs/reference/cookie-domain + description: Domain defaults to the same host that set the cookie. + If you specify the domain explicitly, then subdomains would + also be included. type: string expire: - description: Expire see https://docs.pomerium.com/docs/reference/cookie-expire + description: Expire sets cookie and Pomerium session expiration + time. Once session expires, users would have to re-login. If + you change this parameter, existing sessions are not affected. +

See Session + Management (Enterprise) for a more fine-grained session + controls.

Defaults to 14 hours.

+ format: duration type: string httpOnly: - description: HTTPOnly see https://docs.pomerium.com/docs/reference/cookie-http-only + description: HTTPOnly if set to false, the cookie + would be accessible from within the JavaScript. Defaults to + true. type: boolean name: - description: Name see https://docs.pomerium.com/docs/reference/cookie-name + description: Name sets the Pomerium session cookie name. Defaults + to _pomerium type: string secure: - description: Secure see https://docs.pomerium.com/docs/reference/cookie-secure + description: Secure if set to false, would make a cookie accessible + over insecure protocols (HTTP). Defaults to true. type: boolean type: object identityProvider: - description: IdentityProvider see https://www.pomerium.com/docs/identity-providers/ + description: IdentityProvider configure single-sign-on authentication + and user identity details by integrating with your Identity + Provider properties: provider: - description: Provider one of accepted providers - see https://www.pomerium.com/reference/#identity-provider-name. + description: Provider is the short-hand name of a built-in OpenID + Connect (oidc) identity provider to be used for authentication. + To use a generic provider, set to oidc. enum: - auth0 - azure @@ -100,7 +129,8 @@ spec: type: string refreshDirectory: description: RefreshDirectory is no longer supported, please see - https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync + Upgrade + Guide. properties: interval: description: interval is the time that pomerium will sync @@ -118,29 +148,37 @@ spec: requestParams: additionalProperties: type: string - description: RequestParams see https://www.pomerium.com/reference/#identity-provider-request-params + description: RequestParams to be added as part of a signin request + using OAuth2 code flow. + format: namespace/name type: object requestParamsSecret: description: RequestParamsSecret is a reference to a secret for additional parameters you'd prefer not to provide in plaintext. + format: namespace/name type: string scopes: - description: Scopes see https://www.pomerium.com/reference/#identity-provider-scopes. + description: Scopes Identity provider scopes correspond to access + privilege scopes as defined in Section 3.3 of OAuth 2.0 RFC6749. items: type: string type: array secret: - description: Secret containing IdP provider specific parameters - and must contain at least client_id and client_secret values, - an optional `service_account` field, mapped to https://www.pomerium.com/reference/#identity-provider-service-account + description: Secret containing IdP provider specific parameters. + and must contain at least client_id and client_secret + values. + format: namespace/name minLength: 1 type: string serviceAccountFromSecret: description: ServiceAccountFromSecret is no longer supported, - see https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync + see Upgrade + Guide. type: string url: - description: URL is identity provider url, see https://www.pomerium.com/reference/#identity-provider-url. + description: URL is the base path to an identity provider's OpenID + connect discovery document. See Identity + Providers guides for details. format: uri pattern: ^https:// type: string @@ -152,18 +190,31 @@ spec: additionalProperties: type: string description: JWTClaimHeaders convert claims from the assertion token - into HTTP headers. We recommend you only use it for compatibility - with legacy applications, and use JWT assertion header directly - for new applications, read more at https://www.pomerium.com/docs/topics/getting-users-identity + into HTTP headers and adds them into JWT assertion header. Please + make sure to read + Getting User Identity guide. type: object secrets: - description: Secrets references a Secret that must have the following - keys - shared_secret - cookie_secret - signing_key + description: "Secrets references a Secret with Pomerium bootstrap + parameters. \n

In a default + Pomerium installation manifest, they would be generated via a one-time + job and stored in a pomerium/bootstrap Secret. + You may re-run the job to rotate the secrets, or update the Secret + values manually.

" + format: namespace/name minLength: 1 type: string storage: description: Storage defines persistent storage for sessions and other - data it will use in-memory if none specified see https://www.pomerium.com/docs/topics/data-storage + data. See Storage + for details. If no storage is specified, Pomerium would use a transient + in-memory storage (not recommended for production). properties: postgres: description: Postgres specifies PostgreSQL database connection @@ -171,25 +222,29 @@ spec: properties: caSecret: description: CASecret should refer to a k8s secret with key - `ca.crt` containing CA certificate that, if specified, would - be used to populate `sslrootcert` parameter of the connection - string + ca.crt containing CA certificate that, if specified, + would be used to populate sslrootcert parameter + of the connection string. + format: namespace/name minLength: 1 type: string secret: description: Secret specifies a name of a Secret that must - contain `connection` key for the connection DSN format and - parameters, see https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING - the following keywords are not allowed to be part of the - parameters, as they must be populated via `tlsCecret` and - `caSecret` fields + contain connection key. See DSN + Format and Parameters. Do not set sslrootcert, + sslcert and sslkey via connection + string, use tlsCecret and caSecret + CRD options instead. + format: namespace/name minLength: 1 type: string tlsSecret: description: TLSSecret should refer to a k8s secret of type - `kubernetes.io/tls` and allows to specify an optional client - certificate and key, by constructing `sslcert` and `sslkey` - connection string parameter values see https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS + kubernetes.io/tls and allows to specify an + optional client certificate and key, by constructing sslcert + and sslkey connection string + parameter values. + format: namespace/name minLength: 1 type: string required: @@ -200,25 +255,27 @@ spec: properties: caSecret: description: CASecret should refer to a k8s secret with key - `ca.crt` that must be a PEM-encoded certificate authority - to use when connecting to the databroker storage engine - see https://www.pomerium.com/docs/reference/data-broker-storage-certificate-authority + ca.crt that must be a PEM-encoded certificate + authority to use when connecting to the databroker storage + engine. + format: namespace/name type: string secret: description: Secret specifies a name of a Secret that must - contain `connection` key. see https://www.pomerium.com/docs/reference/data-broker-storage-connection-string + contain connection key. + format: namespace/name minLength: 1 type: string tlsSecret: description: TLSSecret should refer to a k8s secret of type - `kubernetes.io/tls` and allows to specify an optional databroker - storage client certificate and key, see - https://www.pomerium.com/docs/reference/data-broker-storage-certificate-file - - https://www.pomerium.com/docs/reference/data-broker-storage-certificate-key-file + kubernetes.io/tls that would be used to perform + TLS connection to REDIS. + format: namespace/name minLength: 1 type: string tlsSkipVerify: description: TLSSkipVerify disables TLS certificate chain - validation see https://www.pomerium.com/docs/reference/data-broker-storage-tls-skip-verify + validation. type: boolean required: - secret @@ -230,12 +287,12 @@ spec: - secrets type: object status: - description: PomeriumStatus defines the observed state of Settings + description: PomeriumStatus represents configuration and Ingress status. properties: ingress: additionalProperties: description: ResourceStatus represents the outcome of the latest - attempt to reconcile it with Pomerium. + attempt to reconcile relevant Kubernetes resource with Pomerium. properties: error: description: Error that prevented latest observedGeneration @@ -247,7 +304,7 @@ spec: format: date-time type: string observedGeneration: - description: ObservedGeneration represents the .metadata.generation + description: ObservedGeneration represents the .metadata.generation that was last presented to Pomerium. format: int64 type: integer @@ -256,7 +313,7 @@ spec: successfully synced with pomerium. type: boolean warnings: - description: Warnings + description: Warnings while parsing the resource. items: type: string type: array @@ -266,7 +323,7 @@ spec: description: Routes provide per-Ingress status. type: object settingsStatus: - description: settingsStatus represent most recent main configuration + description: SettingsStatus represent most recent main configuration reconciliation status. properties: error: @@ -279,7 +336,7 @@ spec: format: date-time type: string observedGeneration: - description: ObservedGeneration represents the .metadata.generation + description: ObservedGeneration represents the .metadata.generation that was last presented to Pomerium. format: int64 type: integer @@ -288,7 +345,7 @@ spec: successfully synced with pomerium. type: boolean warnings: - description: Warnings + description: Warnings while parsing the resource. items: type: string type: array diff --git a/docs/cmd/main.go b/docs/cmd/main.go new file mode 100644 index 00000000..433c0a5f --- /dev/null +++ b/docs/cmd/main.go @@ -0,0 +1,56 @@ +// Package main is a top level command that generates CRD documentation to the stdout +package main + +import ( + "fmt" + "io" + "log" + "os" + + "github.com/iancoleman/strcase" + + "github.com/pomerium/ingress-controller/docs" +) + +func main() { + if err := generateMarkdown(os.Stdout); err != nil { + log.Fatal(err) + } +} + +func generateMarkdown(w io.Writer) error { + crd, err := docs.Load() + if err != nil { + return fmt.Errorf("loading CRD: %w", err) + } + + tmpl, err := docs.LoadTemplates() + if err != nil { + return fmt.Errorf("loading templates: %w", err) + } + + if err := tmpl.ExecuteTemplate(w, "header", nil); err != nil { + return err + } + + root := crd.Spec.Versions[0].Schema.OpenAPIV3Schema + + for _, key := range []string{"spec", "status"} { + objects, err := docs.Flatten(key, root.Properties[key]) + if err != nil { + return fmt.Errorf("parsing %s: %w", key, err) + } + + fmt.Fprintf(w, "## %s\n", strcase.ToCamel(key)) + if err := tmpl.ExecuteTemplate(w, "object", objects[key]); err != nil { + return fmt.Errorf("exec template: %w", err) + } + delete(objects, key) + + if err := tmpl.ExecuteTemplate(w, "objects", objects); err != nil { + return fmt.Errorf("exec template: %w", err) + } + } + + return nil +} diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 00000000..ab7412c4 --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,2 @@ +// Package docs generates docs from CRD specs +package docs diff --git a/docs/known_formats.go b/docs/known_formats.go new file mode 100644 index 00000000..bc3ecca4 --- /dev/null +++ b/docs/known_formats.go @@ -0,0 +1,18 @@ +package docs + +var ( + knownFormats = map[string]string{ + // standard JSON schema formats + "uri": "an URI as parsed by Golang net/url.ParseRequestURI.", + "hostname": "a valid representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034].", + "ipv4": "an IPv4 IP as parsed by Golang net.ParseIP.", + "ipv6": "an IPv6 IP as parsed by Golang net.ParseIP.", + "cidr": "a CIDR as parsed by Golang net.ParseCIDR.", + "byte": "base64 encoded binary data.", + "date": `a date string like "2006-01-02" as defined by full-date in RFC3339.`, + "duration": `a duration string like "22s" as parsed by Golang time.ParseDuration.`, + "date-time": `a date time string like "2014-12-15T19:30:20.000Z" as defined by date-time in RFC3339.`, + // pomerium formats + "namespace/name": `reference to Kubernetes resource with namespace prefix: namespace/name format.`, + } +) diff --git a/docs/parse.go b/docs/parse.go new file mode 100644 index 00000000..16e6b7b8 --- /dev/null +++ b/docs/parse.go @@ -0,0 +1,154 @@ +package docs + +import ( + "bytes" + "fmt" + + extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/util/yaml" + + "github.com/pomerium/ingress-controller/config/crd" +) + +// Object is a simplified representation of JSON Schema Object +type Object struct { + ID string + Description string + Properties map[string]*Property +} + +// Property is an Object property, that may be either an atomic value, reference to an object or a map +type Property struct { + ID string + Description string + Required bool + + ObjectOrAtomic + Map *ObjectOrAtomic +} + +// Atomic is a base type +type Atomic struct { + Format string + Type string +} + +// ExplainFormat returns a human readable explanation for a known format, i.e. date-time +func (a *Atomic) ExplainFormat() *string { + if txt, ok := knownFormats[a.Format]; ok { + return &txt + } + return nil +} + +// ObjectOrAtomic represents either an object reference or an atomic value +type ObjectOrAtomic struct { + // ObjectRef if set, represents a reference to an object key + ObjectRef *string + // Atomic if set, represents an atomic type + Atomic *Atomic +} + +// Load parses CRD document from Yaml spec +func Load() (*extv1.CustomResourceDefinition, error) { + dec := yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(crd.SettingsCRD), 100) + var spec extv1.CustomResourceDefinition + if err := dec.Decode(&spec); err != nil { + return nil, err + } + return &spec, nil +} + +// Flatten parses the JSON Schema and returns a flattened list of referenced objects +func Flatten(key string, src extv1.JSONSchemaProps) (map[string]*Object, error) { + objects := make(map[string]*Object) + if err := flatten(key, src, objects); err != nil { + return nil, err + } + return objects, nil +} + +func flatten(key string, src extv1.JSONSchemaProps, objects map[string]*Object) error { + obj := &Object{ + ID: key, + Description: src.Description, + Properties: make(map[string]*Property), + } + + atomicHandler := func(key string, prop extv1.JSONSchemaProps) (*Property, error) { + return &Property{ObjectOrAtomic: ObjectOrAtomic{Atomic: atomic(prop)}}, nil + } + + arrayHandler := func(key string, prop extv1.JSONSchemaProps) (*Property, error) { + return &Property{ObjectOrAtomic: ObjectOrAtomic{Atomic: array(prop)}}, nil + } + + typeHandler := map[string]func(key string, prop extv1.JSONSchemaProps) (*Property, error){ + "object": func(key string, prop extv1.JSONSchemaProps) (*Property, error) { + if prop.AdditionalProperties != nil { + // this is a map + prop = *prop.AdditionalProperties.Schema + if prop.Type == "object" { + // register map value type under the name of this key + if err := flatten(key, prop, objects); err != nil { + return nil, err + } + return &Property{Map: &ObjectOrAtomic{ObjectRef: &key}}, nil + } + return &Property{Map: &ObjectOrAtomic{Atomic: atomic(prop)}}, nil + } + if err := flatten(key, prop, objects); err != nil { + return nil, err + } + return &Property{ObjectOrAtomic: ObjectOrAtomic{ObjectRef: &key}}, nil + }, + "string": atomicHandler, + "boolean": atomicHandler, + "integer": atomicHandler, + "array": arrayHandler, + } + + for key, prop := range src.Properties { + fn, ok := typeHandler[prop.Type] + if !ok { + fmt.Printf("don't know how to handle type %s\n", prop.Type) + continue + } + val, err := fn(key, prop) + if err != nil { + return fmt.Errorf("%s: %w", key, err) + } + val.ID = key + val.Description = prop.Description + obj.Properties[key] = val + } + + for _, key := range src.Required { + prop, ok := obj.Properties[key] + if !ok { + return fmt.Errorf("required field %s not found", key) + } + prop.Required = true + } + + if _, ok := objects[key]; ok { + return fmt.Errorf("cannot flatten: duplicate key %s", key) + } + objects[key] = obj + + return nil +} + +func atomic(src extv1.JSONSchemaProps) *Atomic { + return &Atomic{ + Format: src.Format, + Type: src.Type, + } +} + +func array(src extv1.JSONSchemaProps) *Atomic { + return &Atomic{ + Format: src.Format, + Type: fmt.Sprintf("[]%s", src.Items.Schema.Type), + } +} diff --git a/docs/template.go b/docs/template.go new file mode 100644 index 00000000..c10df187 --- /dev/null +++ b/docs/template.go @@ -0,0 +1,18 @@ +package docs + +import ( + "embed" + "strings" + + "text/template" +) + +//go:embed templates/*.tmpl +var templateFS embed.FS + +// LoadTemplates would load all templates from `./templates` +func LoadTemplates() (*template.Template, error) { + return template.New("root").Funcs(template.FuncMap{ + "anchor": strings.ToLower, + }).ParseFS(templateFS, "templates/*") +} diff --git a/docs/templates/header.tmpl b/docs/templates/header.tmpl new file mode 100644 index 00000000..73ddca0a --- /dev/null +++ b/docs/templates/header.tmpl @@ -0,0 +1,17 @@ +{{define "header"}}--- +title: Kubernetes Deployment Reference +sidebar_label: Reference +description: Reference for Pomerium settings in Kubernetes deployments. +--- + +Pomerium-specific parameters should be configured via the `ingress.pomerium.io/Pomerium` CRD. +The default Pomerium deployment is listening to the CRD `global`, that may be customized via command line parameters. + +Pomerium posts updates to the CRD `/status`: +```shell +kubectl describe pomerium +``` + +Kubernetes-specific deployment parameters should be added via `kustomize` to the manifests. + +{{end}} diff --git a/docs/templates/object-properties.tmpl b/docs/templates/object-properties.tmpl new file mode 100644 index 00000000..ca99e1da --- /dev/null +++ b/docs/templates/object-properties.tmpl @@ -0,0 +1,38 @@ +{{define "object-properties"}}{{if .}} + + + + + {{range .}} + + + + {{end}} + +
+

+ {{.ID}}   + {{if .ObjectRef}} + object  + ({{.ObjectRef}}) + {{else if and .Atomic .Atomic.ExplainFormat}} + {{.Atomic.Type}}  + ({{.Atomic.Format}}) + {{else if .Atomic}} + {{.Atomic.Type}}  + {{else if .Map.Atomic}} + map[string]{{.Map.Atomic.Type}} + {{else if .Map.ObjectRef}} + map[string] + {{.Map.ObjectRef}} + {{end}} +

+

+ {{if .Required}}Required. {{end}} + {{.Description}} +

+ {{if and .Atomic .Atomic.ExplainFormat}} + Format: {{.Atomic.ExplainFormat}} + {{end}} +
+{{end}}{{end}} diff --git a/docs/templates/object.tmpl b/docs/templates/object.tmpl new file mode 100644 index 00000000..797ae887 --- /dev/null +++ b/docs/templates/object.tmpl @@ -0,0 +1,4 @@ +{{define "object"}} +{{.Description}} +{{template "object-properties" .Properties}} +{{end}} diff --git a/docs/templates/objects.tmpl b/docs/templates/objects.tmpl new file mode 100644 index 00000000..9f52205d --- /dev/null +++ b/docs/templates/objects.tmpl @@ -0,0 +1,6 @@ +{{define "objects"}} +{{range .}} +### `{{.ID}}` +{{template "object" .}} +{{end}} +{{end}} diff --git a/go.mod b/go.mod index 457e5414..33844b30 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.25.3 + k8s.io/apiextensions-apiserver v0.25.0 k8s.io/apimachinery v0.25.3 k8s.io/apiserver v0.25.3 k8s.io/client-go v0.25.3 @@ -314,7 +315,6 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect honnef.co/go/tools v0.3.3 // indirect - k8s.io/apiextensions-apiserver v0.25.0 // indirect k8s.io/component-base v0.25.3 // indirect k8s.io/klog/v2 v2.70.1 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect