blueprints: better OCI support in UI (#4263)
use oci:// prefix to detect oci blueprint, add UI support Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		| @ -92,7 +92,7 @@ class BlueprintInstance(SerializerModel, ManagedModel, CreatedUpdatedModel): | ||||
|         if ":" in url.path: | ||||
|             path, _, ref = path.partition(":") | ||||
|         client = NewClient( | ||||
|             f"{url.scheme}://{url.hostname}", | ||||
|             f"https://{url.hostname}", | ||||
|             WithUserAgent(authentik_user_agent()), | ||||
|             WithUsernamePassword(url.username, url.password), | ||||
|             WithDefaultName(path), | ||||
| @ -135,12 +135,11 @@ class BlueprintInstance(SerializerModel, ManagedModel, CreatedUpdatedModel): | ||||
|  | ||||
|     def retrieve(self) -> str: | ||||
|         """Retrieve blueprint contents""" | ||||
|         if self.path.startswith("oci://"): | ||||
|             return self.retrieve_oci() | ||||
|         full_path = Path(CONFIG.y("blueprints_dir")).joinpath(Path(self.path)) | ||||
|         if full_path.exists(): | ||||
|             LOGGER.debug("Blueprint path exists locally", instance=self) | ||||
|             with full_path.open("r", encoding="utf-8") as _file: | ||||
|                 return _file.read() | ||||
|         return self.retrieve_oci() | ||||
|         with full_path.open("r", encoding="utf-8") as _file: | ||||
|             return _file.read() | ||||
|  | ||||
|     @property | ||||
|     def serializer(self) -> Serializer: | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import { docLink } from "@goauthentik/common/global"; | ||||
| import { first } from "@goauthentik/common/utils"; | ||||
| import "@goauthentik/elements/CodeMirror"; | ||||
| import "@goauthentik/elements/forms/FormGroup"; | ||||
| @ -8,19 +9,37 @@ import YAML from "yaml"; | ||||
|  | ||||
| import { t } from "@lingui/macro"; | ||||
|  | ||||
| import { TemplateResult, html } from "lit"; | ||||
| import { customElement } from "lit/decorators.js"; | ||||
| import { CSSResult, TemplateResult, css, html } from "lit"; | ||||
| import { customElement, state } from "lit/decorators.js"; | ||||
| import { ifDefined } from "lit/directives/if-defined.js"; | ||||
| import { until } from "lit/directives/until.js"; | ||||
|  | ||||
| import PFContent from "@patternfly/patternfly/components/Content/content.css"; | ||||
| import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css"; | ||||
|  | ||||
| import { BlueprintInstance, ManagedApi } from "@goauthentik/api"; | ||||
|  | ||||
| enum blueprintSource { | ||||
|     local, | ||||
|     oci, | ||||
| } | ||||
|  | ||||
| @customElement("ak-blueprint-form") | ||||
| export class BlueprintForm extends ModelForm<BlueprintInstance, string> { | ||||
|     @state() | ||||
|     source: blueprintSource = blueprintSource.local; | ||||
|  | ||||
|     loadInstance(pk: string): Promise<BlueprintInstance> { | ||||
|         return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsRetrieve({ | ||||
|             instanceUuid: pk, | ||||
|         }); | ||||
|         return new ManagedApi(DEFAULT_CONFIG) | ||||
|             .managedBlueprintsRetrieve({ | ||||
|                 instanceUuid: pk, | ||||
|             }) | ||||
|             .then((inst) => { | ||||
|                 if (inst.path.startsWith("oci://")) { | ||||
|                     this.source = blueprintSource.oci; | ||||
|                 } | ||||
|                 return inst; | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     getSuccessMessage(): string { | ||||
| @ -31,6 +50,18 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return super.styles.concat( | ||||
|             PFToggleGroup, | ||||
|             PFContent, | ||||
|             css` | ||||
|                 .pf-c-toggle-group { | ||||
|                     justify-content: center; | ||||
|                 } | ||||
|             `, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     send = (data: BlueprintInstance): Promise<BlueprintInstance> => { | ||||
|         if (this.instance?.pk) { | ||||
|             return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsUpdate({ | ||||
| @ -65,27 +96,94 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> { | ||||
|                 </div> | ||||
|                 <p class="pf-c-form__helper-text">${t`Disabled blueprints are never applied.`}</p> | ||||
|             </ak-form-element-horizontal> | ||||
|             <ak-form-element-horizontal label=${t`Path`} name="path"> | ||||
|                 <select class="pf-c-form-control"> | ||||
|                     ${until( | ||||
|                         new ManagedApi(DEFAULT_CONFIG) | ||||
|                             .managedBlueprintsAvailableList() | ||||
|                             .then((files) => { | ||||
|                                 return files.map((file) => { | ||||
|                                     let name = file.path; | ||||
|                                     if (file.meta && file.meta.name) { | ||||
|                                         name = `${name} (${file.meta.name})`; | ||||
|                                     } | ||||
|                                     const selected = file.path === this.instance?.path; | ||||
|                                     return html`<option ?selected=${selected} value=${file.path}> | ||||
|                                         ${name} | ||||
|                                     </option>`; | ||||
|                                 }); | ||||
|                             }), | ||||
|                         html`<option>${t`Loading...`}</option>`, | ||||
|                     )} | ||||
|                 </select> | ||||
|             </ak-form-element-horizontal> | ||||
|             <div class="pf-c-card pf-m-selectable pf-m-selected"> | ||||
|                 <div class="pf-c-card__body"> | ||||
|                     <div class="pf-c-toggle-group"> | ||||
|                         <div class="pf-c-toggle-group__item"> | ||||
|                             <button | ||||
|                                 class="pf-c-toggle-group__button ${this.source === | ||||
|                                 blueprintSource.local | ||||
|                                     ? "pf-m-selected" | ||||
|                                     : ""}" | ||||
|                                 type="button" | ||||
|                                 @click=${() => { | ||||
|                                     this.source = blueprintSource.local; | ||||
|                                 }} | ||||
|                             > | ||||
|                                 <span class="pf-c-toggle-group__text">${t`Local path`}</span> | ||||
|                             </button> | ||||
|                         </div> | ||||
|                         <div class="pf-c-divider pf-m-vertical" role="separator"></div> | ||||
|                         <div class="pf-c-toggle-group__item"> | ||||
|                             <button | ||||
|                                 class="pf-c-toggle-group__button ${this.source === | ||||
|                                 blueprintSource.oci | ||||
|                                     ? "pf-m-selected" | ||||
|                                     : ""}" | ||||
|                                 type="button" | ||||
|                                 @click=${() => { | ||||
|                                     this.source = blueprintSource.oci; | ||||
|                                 }} | ||||
|                             > | ||||
|                                 <span class="pf-c-toggle-group__text">${t`OCI Registry`}</span> | ||||
|                             </button> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="pf-c-card__footer"> | ||||
|                     ${this.source === blueprintSource.local | ||||
|                         ? html`<ak-form-element-horizontal label=${t`Path`} name="path"> | ||||
|                               <select class="pf-c-form-control"> | ||||
|                                   ${until( | ||||
|                                       new ManagedApi(DEFAULT_CONFIG) | ||||
|                                           .managedBlueprintsAvailableList() | ||||
|                                           .then((files) => { | ||||
|                                               return files.map((file) => { | ||||
|                                                   let name = file.path; | ||||
|                                                   if (file.meta && file.meta.name) { | ||||
|                                                       name = `${name} (${file.meta.name})`; | ||||
|                                                   } | ||||
|                                                   const selected = | ||||
|                                                       file.path === this.instance?.path; | ||||
|                                                   return html`<option | ||||
|                                                       ?selected=${selected} | ||||
|                                                       value=${file.path} | ||||
|                                                   > | ||||
|                                                       ${name} | ||||
|                                                   </option>`; | ||||
|                                               }); | ||||
|                                           }), | ||||
|                                       html`<option>${t`Loading...`}</option>`, | ||||
|                                   )} | ||||
|                               </select></ak-form-element-horizontal | ||||
|                           >` | ||||
|                         : html``} | ||||
|                     ${this.source === blueprintSource.oci | ||||
|                         ? html`<ak-form-element-horizontal label=${t`URL`} name="path"> | ||||
|                               <input | ||||
|                                   type="text" | ||||
|                                   value="${ifDefined(this.instance?.path)}" | ||||
|                                   class="pf-c-form-control" | ||||
|                                   required | ||||
|                               /> | ||||
|                               <p class="pf-c-form__helper-text"> | ||||
|                                   ${t`OCI URL, in the format of oci://registry.domain.tld/path/to/manifest.`} | ||||
|                               </p> | ||||
|                               <p class="pf-c-form__helper-text"> | ||||
|                                   ${t`See more about OCI support here:`}  | ||||
|                                   <a | ||||
|                                       target="_blank" | ||||
|                                       href="${docLink( | ||||
|                                           "/developer-docs/blueprints/?utm_source=authentik#storage---oci", | ||||
|                                       )}" | ||||
|                                       >${t`Documentation`}</a | ||||
|                                   > | ||||
|                               </p> | ||||
|                           </ak-form-element-horizontal>` | ||||
|                         : html``} | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             <ak-form-group> | ||||
|                 <span slot="header">${t`Additional settings`}</span> | ||||
|                 <div slot="body" class="pf-c-form"> | ||||
|  | ||||
| @ -33,7 +33,7 @@ To disable existing blueprints, an empty file can be mounted over the existing b | ||||
|  | ||||
| Blueprints can also be stored in remote [OCI](https://opencontainers.org/) compliant registries. This includes GitHub Container Registry, Docker hub and many other registries. | ||||
|  | ||||
| To download a blueprint via OCI, set the path to `https://ghcr.io/<username>/<package-name>:<ref>`. This will fetch the blueprint from an OCI package hosted on GHCR. | ||||
| To download a blueprint via OCI, set the path to `oci://ghcr.io/<username>/<package-name>:<ref>`. This will fetch the blueprint from an OCI package hosted on GHCR. | ||||
|  | ||||
| To fetch blueprints from a private registry with authentication, credentials can be embedded into the URL. | ||||
|  | ||||
|  | ||||
| @ -3,6 +3,12 @@ title: Release 2022.12 | ||||
| slug: "2022.12" | ||||
| --- | ||||
|  | ||||
| ## Breaking changes | ||||
|  | ||||
| -   Blueprints fetched via OCI require oci:// schema | ||||
|  | ||||
|     To better detect if a blueprint should be fetched locally or via OCI, all OCI sourced blueprints require an `oci://` protocol. | ||||
|  | ||||
| ## New features | ||||
|  | ||||
| -   Bundled GeoIP City database | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L