From eb4c39f4e70a7c8a8148b761da81c739db048c0d Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Fri, 16 Sep 2022 16:38:14 +0100 Subject: [PATCH] Prospero user documentation --- dist/docs/guide/channels/index.adoc | 194 ++++++++++++++++++ dist/docs/guide/concepts/channels.adoc | 71 +++++++ dist/docs/guide/concepts/galleon.adoc | 9 + dist/docs/guide/concepts/index.adoc | 11 + .../customization/apply-customization.adoc | 2 +- .../customization/customization-bundle.adoc | 72 ++++++- dist/docs/guide/customization/index.adoc | 10 +- .../customization/promote-customization.adoc | 49 ++--- .../customization/registering-channel.adoc | 23 ++- dist/docs/guide/index.adoc | 14 ++ dist/docs/guide/metadata/index.adoc | 18 ++ dist/docs/guide/overview/index.adoc | 17 ++ dist/docs/guide/process/index.adoc | 49 +++++ dist/docs/guide/process/overview.png | Bin 0 -> 33448 bytes dist/docs/guide/usage/channel_mgmt.adoc | 54 +++++ dist/docs/guide/usage/clone.adoc | 62 ++++++ dist/docs/guide/usage/history.adoc | 102 +++++++++ dist/docs/guide/usage/index.adoc | 11 + dist/docs/guide/usage/install.adoc | 95 +++++++++ dist/docs/guide/usage/update.adoc | 74 +++++++ dist/docs/pom.xml | 97 ++++----- 21 files changed, 939 insertions(+), 95 deletions(-) create mode 100644 dist/docs/guide/channels/index.adoc create mode 100644 dist/docs/guide/concepts/channels.adoc create mode 100644 dist/docs/guide/concepts/galleon.adoc create mode 100644 dist/docs/guide/concepts/index.adoc create mode 100644 dist/docs/guide/metadata/index.adoc create mode 100644 dist/docs/guide/overview/index.adoc create mode 100644 dist/docs/guide/process/index.adoc create mode 100644 dist/docs/guide/process/overview.png create mode 100644 dist/docs/guide/usage/channel_mgmt.adoc create mode 100644 dist/docs/guide/usage/clone.adoc create mode 100644 dist/docs/guide/usage/history.adoc create mode 100644 dist/docs/guide/usage/index.adoc create mode 100644 dist/docs/guide/usage/install.adoc create mode 100644 dist/docs/guide/usage/update.adoc diff --git a/dist/docs/guide/channels/index.adoc b/dist/docs/guide/channels/index.adoc new file mode 100644 index 000000000..2e8eb9d37 --- /dev/null +++ b/dist/docs/guide/channels/index.adoc @@ -0,0 +1,194 @@ +## Working with channels + +More information on the Wildfly Channels can be found in https://github.com/wildfly-extras/wildfly-channel/blob/main/doc/spec.adoc. This chapter is intended to explain some concepts behind different channel types and how they can be used to provide software updates. + +A channel contains a collections of artifact streams used by a server. Each stream should contain only backwards-compatible artifact updates with new versions of artifacts replacing previous ones. + +There are two ways for a stream to determine the versions of its artifact: + + * using an artifact manifest with fixed versions, or + * use the latest artifact version available in backing Maven repository. + +NOTE: The two mechanisms can be mixed within one channel, using static list to resolve some streams and Maven metadata for others. + +Channels using exclusively fixed version manifest can be more stable and make it possible to use 3rd party repositories. The artifact combination in the manifest can be tested before distributing, making sure there are no regressions. + +On the other hand channels using Maven metadata can make development and testing of new component updates easier. Any component updates deployed into the Maven repository are immediately made available to the subscribed servers. + +### Channels with fixed version manifests + +This kind of channels rely on manifests to list all available artifacts and their versions. This can be useful when the content of the repository cannot be fully controlled and might contain incompatible artifact versions. + +The manifest is a YAML file, containing a list of available streams: + +[source, yaml, title="manifest.yaml"] +``` +schemaVersion: "1.0.0" +name: "test-manifest" +streams: + - groupId: "org.test" + artifactId: "artifact-one" + version: "1.2.0.Final" +``` + +The manifest has to be deployed in the channel repository and registered in the channel definition: + +[source, yaml, title="channel.yaml"] +``` +schemaVersion: "2.0.0" +name: "test-channel" +resolve-if-no-stream: none #(1) +repositories: + - id: "trusted" + url: "https://trusted.repository.org/maven/" +manifest: + maven: + groupId: org.test.channel + artifactId: test-manifest +``` +<1> this channel provides only artifacts explicitly listed in the manifest + +#### Updating components in fixed version channel + +Updating a component in a manifest channel requires publishing a new version of manifest. + +For example, if the `org.test:artifact-one` is updated to `1.2.1.Final` version, the new manifest would look like as follows: + +[source, yaml, title="manifest.yaml"] +``` +schemaVersion: "1.0.0" +name: "test-manifest" +streams: + - groupId: "org.test" + artifactId: "artifact-one" + version: "1.2.1.Final" +``` + +This manifest has to be published in the channel repository with a new version: + +``` +mvn deploy:deploy-file -Dfile=manifest.yaml \ +-DgroupId=org.test.channels -DartifactId=test-manifest \ +-Dversion=1.0.1 \ #(1) +-Dclassifier=manifest -Dpackaging=yaml \ +-Durl=https://trusted.repository.org/maven/ +``` +<1> note the updated version + +There are no changes required to the channel definition. Next time components are resolved from this channel, a new version of manifest will be used providing version *1.2.1.Final* of `org.test:artifact-one`. + +### Channels using backing Maven repository versions + +This type of channels expose the latest available versions of artifacts found in their repositories as the component version. The versions can be curated using either version patterns or a blocklist excluding specific versions. + +Channels based on Maven metadata can be useful when the content of repositories can be controlled and trusted. Any new artifact deployed into the channel repository, will automatically be made available for subscribed servers. + +An example of channel exposing all underlying artifacts can be seen below: + +[source, yaml, title="channel.yaml"] +``` +schemaVersion: "2.0.0" +name: "test-channel" +resolve-if-no-stream: latest #(1) +repositories: #(2) + - id: "trusted" + url: "https://trusted.repository.org/maven/" +``` +<1> `latest` strategy means that if channel cannot find a stream matching the requested artifact, it will attempt to find the latest version in its repositories +<2> the underlying repository for the channel + +#### Using version patterns to limit artifacts + +Sometimes the underlying repository might contain versions of artifacts that are not compatible with the installed server. For example the server might require a *1.2.x* version of a certain artifact, but the repository contains a newer version *2.0.x*. + +To filter incompatible versions, the channel can use a manifest file with `versionPattern` streams. + +[source, yaml, title="manifest.yaml"] +``` +schemaVersion: "1.0.0" +name: "test-manifest" +streams: + - groupId: "org.test" + artifactId: "artifact-one" + versionPattern: "1\\.2\\..*" #(1) +``` +<1> a https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[regular expression] limiting versions to ones starting with `1.2.` + +NOTE: The `versionPattern` field uses regular expressions + +The above manifest file should be deployed in the channel repository with a classifier `manifest` and extension `yaml`. For example following command will deploy the file as `org.test.channels:test-manifest:1.0.0` + +``` +mvn deploy:deploy-file -Dfile=manifest.yaml \ + -DgroupId=org.test.channels -DartifactId=test-manifest \ + -Dversion=1.0.0 -Dclassifier=manifest -Dpackaging=yaml \ + -Durl=https://trusted.repository.org/maven/ +``` + +The channel definition needs to be updated to reference the new manifest file: + +[source, yaml, title="channel.yaml"] +``` +schemaVersion: "2.0.0" +name: "test-channel" +resolve-if-no-stream: latest +repositories: + - id: "trusted" + url: "https://trusted.repository.org/maven/" +manifest: + maven: + groupId: org.test.channel + artifactId: test-manifest +``` + +Using this channel definition, all artifacts apart from `org.test:artifact-one` are still resolved to the latest versions available in the repository. The `org.test:artifact-one` will be resolved to the latest available "1.2.x" micro version. For example, if the repository contains versions [*1.2.0.Final*, *1.2.1.Final*, *2.0.0.Final*], the channel will pick version *1.2.1.Final*. + +#### Creating blocklist to exclude updates + +Another option to exclude certain artifact versions is to use a blocklist. A blocklist is a YAML file deployed in the channel repository listing blocked artifact versions. + +[source, yaml, title="blocklist.yaml"] +``` +schemaVersion: "1.0.0" +name: "test-blocklist" +blocks: + - groupId: "org.test" + artifactId: "artifact-one" + versions: + - "1.2.2.Final" +``` + +Again, the blocklist has to be deployed in the channel repository. The blocklist artifact has to use `blocklist` classifier and `yaml` extension. For example: + +``` +mvn deploy:deploy-file -Dfile=blocklist.yaml \ + -DgroupId=org.test.channels -DartifactId=test-blocklist \ + -Dversion=1.0.0 -Dclassifier=blocklist -Dpackaging=yaml \ + -Durl=https://trusted.repository.org/maven/ +``` + +Finally, the channel definition has to be updated with the reference to the blocklist: + +[source, yaml, title="channel.yaml"] +``` +schemaVersion: "2.0.0" +name: "test-channel" +resolve-if-no-stream: latest +blocklist: + maven: + groupId: org.test.channel + artifactId: test-blocklist +repositories: + - id: "trusted" + url: "https://trusted.repository.org/maven/" +manifest: + maven: + groupId: org.test.channel + artifactId: test-manifest +``` + +Resolving `org.test:artifact-one` from this channel will exclude any versions not matching "1.2.*" pattern and version 1.2.2.Final. For example, if the repository contains versions [*1.2.0.Final*, *1.2.1.Final*, *1.2.2.Final*, *2.0.0.Final*], the channel will pick version *1.2.1.Final*. + +#### Updating components using "open" channel + +Updating component in an open channel requires only deploying the artifact into the channel repository. Neither channel definition not channel manifest has to be changed. Next time components are resolved from this channel, a new version of updated component will be used. \ No newline at end of file diff --git a/dist/docs/guide/concepts/channels.adoc b/dist/docs/guide/concepts/channels.adoc new file mode 100644 index 000000000..af48824ad --- /dev/null +++ b/dist/docs/guide/concepts/channels.adoc @@ -0,0 +1,71 @@ +### Channels + +Wildfly Channels are a way to provide a curated list of compatible, up-to-date components. A channel consists of a YAML definition file and one or more underlying Maven repositories. + +There are two types of Wildfly Channels supported by Prospero - open channels and manifest channels. Open channels allow access to the latest artifacts in the underlying repository, while manifest channels define a list of allowed list of components. + +For more information on Wildfly Channels see the https://github.com/wildfly-extras/wildfly-channel/blob/main/doc/spec.adoc[spec documentation]. + +#### Manifest channels + +One way to specify channel content is by using manifests. Manifests are YAML file listing streams available in the channel. For example a part of wildfly-27.0.0 manifest looks like: + +[source, yaml] +---- +schemaVersion: "1.0.0" +name: Manifest for WildFly 27 +description: |- + This manifest provides updates for WildFly 27 Feature Pack. +streams: + - groupId: "org.wildfly" + artifactId: "wildfly-galleon-pack" + version: "27.0.0.Final" + + - groupId: "org.wildfly" + artifactId: "wildfly-ee-galleon-pack" + version: "27.0.0.Final" + + - groupId: "org.wildfly.core" + artifactId: "wildfly-core-galleon-pack" + version: "19.0.0.Beta13" + + - groupId: "org.wildfly.core" + artifactId: "wildfly-version" + version: "19.0.0.Final" + + - groupId: "org.wildfly.security" + artifactId: "wildfly-elytron-jaspi" + version: "1.20.2.Final" +---- + +The channel using this manifest would be defined as: +[source, yaml] +---- +schemaVersion: "2.0.0" +name: Channel for WildFly 27 +manifest: + maven: + groupId: org.wildfly.channels + artifactId: wildfly-27.0 +repositories: + - id: central + url: https://repo1.maven.org/maven2/ +---- + +When resolving artifacts using such channel, the versions of artifacts will be dictated by the manifest, even if newer version is available in the channel repositories. + +NOTE: The manifest can be distributed as either a static file (referenced in a channel by a URL), or as an artifact in a Maven repository. + +#### Open channels + +Alternatively the channel can be defined to use the latest versions of artifacts available in its repositories. + +[source, yaml] +---- +schemaVersion: "2.0.0" +name: Channel for WildFly 27 +resolve-if-no-stream: latest +repositories: + - id: central + url: https://repo1.maven.org/maven2/ +---- \ No newline at end of file diff --git a/dist/docs/guide/concepts/galleon.adoc b/dist/docs/guide/concepts/galleon.adoc new file mode 100644 index 000000000..8b74048ba --- /dev/null +++ b/dist/docs/guide/concepts/galleon.adoc @@ -0,0 +1,9 @@ +### Feature Packs + +Galleon is a provisioning tool used to assemble Wildfly distributions. In order for a piece of software to be provisioned by Galleon it needs to be packaged as a feature-pack. + +Feature packs are ZIP archives usually available in Maven repositories. They contain information about installed software's filesystem content, configuration and additional tasks needed to assemble and configure it. + +In case of Wildfly server, the feature pack also contains a list of Maven coordinates for all the components included in the server distribution. During provisioning, those coordinates are used to download the components and place them in resulting server. + +For more information on Galleon and the Feature Packs see the https://docs.wildfly.org/galleon/#_feature_packs[Galleon documentation] \ No newline at end of file diff --git a/dist/docs/guide/concepts/index.adoc b/dist/docs/guide/concepts/index.adoc new file mode 100644 index 000000000..61acc6d0e --- /dev/null +++ b/dist/docs/guide/concepts/index.adoc @@ -0,0 +1,11 @@ +## Concepts + +The Wildfly servers are assembled from a large number of dependencies from various projects. If any of those components is changed, in the traditional update process, the server has to be rebuilt with updated components and distributed as a new version. + +Prospero aims to simplify this process by allowing users to apply updated components to already provisioned servers. + +To achieve that, Prospero utilizes two projects - Galleon and Wildfly Channels. + +include::galleon.adoc[] + +include::channels.adoc[] \ No newline at end of file diff --git a/dist/docs/guide/customization/apply-customization.adoc b/dist/docs/guide/customization/apply-customization.adoc index f2f66b25f..d21dafba5 100644 --- a/dist/docs/guide/customization/apply-customization.adoc +++ b/dist/docs/guide/customization/apply-customization.adoc @@ -1,6 +1,6 @@ ### Apply changes to the server -After promoting artifacts to the channel, the changes can be applied to targeted servers. +After promoting artifacts to the channel, the changes can be applied to subscribed servers. Promoted changes are treated as any updates, so `prospero update` command can be used to apply them. diff --git a/dist/docs/guide/customization/customization-bundle.adoc b/dist/docs/guide/customization/customization-bundle.adoc index d81eb7782..366f149ff 100644 --- a/dist/docs/guide/customization/customization-bundle.adoc +++ b/dist/docs/guide/customization/customization-bundle.adoc @@ -1,10 +1,10 @@ ### Distributing custom artifacts -The customized artifacts can be distributed as bundles. Each bundle is a ZIP archive containing an `artifact-list.yaml` file and a `repository` directory. +The customized artifacts can be distributed as bundles. Each bundle is a ZIP archive containing a `maven-repository` directory and an optional `artifact-list.yaml` file. ``` |-artifact-list.yaml -|-repository +|-maven-repository |- org |-custom |-artifact-one @@ -13,8 +13,30 @@ The customized artifacts can be distributed as bundles. Each bundle is a ZIP arc |-artifact-two |-2.3.4 |-artifact-two-2.3.4.jar + |-wildfly + |-prospero + |-customizations + |-1.0.0 + |-customizations-manifest-1.0.0.yaml + |-maven-metadata.xml ``` +In addition to the custom artifacts, repository has to contain a channel manifest. The manifest should list all streams available in the customization bundle. + +``` +schemaVersion: 1.0.0 +name: Customizations manifest +streams: + - groupId: org.custom + artifactId: artifact-one + version: 1.2.3 + - groupId: org.custom + artifactId: artifact-two + version: 2.3.4 +``` + +NOTE: the Maven metadata has to be present for the manifest artifact. + The `artifact-list.yaml` lists all artifacts included in the bundle using following syntax: ``` artifacts: @@ -23,4 +45,48 @@ artifacts: version: "1.2.3" packaging: "jar" extension: "" -``` \ No newline at end of file +``` + +#### Updating custom artifacts + +If a new version of customization bundle needs to be distributed, it should contain a customization manifest deployed under the same `groupId:artifactId` coordinates, but with an incremented version. + +The repository should contain all customized artifacts required by the updated manifest. For instance, the updated manifest included a new version of `artifact-one` and added `artifact-three`, resulting in following file: + +``` +schemaVersion: 1.0.0 +name: Customizations manifest +streams: + - groupId: org.custom + artifactId: artifact-one + version: 1.2.4 + - groupId: org.custom + artifactId: artifact-two + version: 2.3.4 + - groupId: org.custom + artifactId: artifact-three + version: 3.4.5 +``` + +The updated repository should contain three artifacts: +``` +|-artifact-list.yaml +|-maven-repository + |- org + |-custom + |-artifact-one + |-1.2.4 + |-artifact-one-1.2.4.jar + |-artifact-two + |-2.3.4 + |-artifact-two-2.3.4.jar + |-artifact-three + |-3.4.5 + |-artifact-two-3.4.5.jar + |-wildfly + |-prospero + |-customizations + |-1.0.1 + |-customizations-manifest-1.0.1.yaml + |-maven-metadata.xml +``` diff --git a/dist/docs/guide/customization/index.adoc b/dist/docs/guide/customization/index.adoc index 282eb29a4..8332ee4ab 100644 --- a/dist/docs/guide/customization/index.adoc +++ b/dist/docs/guide/customization/index.adoc @@ -1,10 +1,16 @@ ## Custom channels -Sometimes a server (or group of servers) need to be updated with different versions of components than other servers. To achieve that, a server can subscribe to a custom channel on top of normal channels. This channel will define modified versions of components. +Sometimes a server needs to be updated with different versions of components than other servers. To achieve that, a server can subscribe to a custom channel on top of its normal channels. This channel will define modified versions of components. For example if the main channel defines component `foo:bar:1.2.3`, the custom channel could contain `foo:bar:1.2.3-patch0001`. During any update operations, the most recent version of the `foo:bar` across both channels will be applied to the server. -The custom channel needs to be hosted in a Maven repository - either a local, filesystem repository on the same system as the server, or a web repository shared between a group of servers. The repository is used to host a versioned channel definition and all customized artifacts. +The custom channel needs to be hosted in a Maven repository - either a local filesystem repository on the same system as the server, or a web repository shared between a group of servers. The repository is used to host a versioned channel definition and all customized artifacts. + +### Custom artifact versioning + +When multiple versions of the same artifact are available from different channels, Prospero will always choose the latest available version of this artifact. + +Therefore, the customized artifacts have to always have higher versions than artifacts they are replacing. For instance, if a server includes `my:artifact:1.1.0`, the customization channel can provide a version `my:artifact:1.1.0-path-00001` or `my:artifact:1.1.1`, but not `my:artifact:1.0.9`. include::registering-channel.adoc[] diff --git a/dist/docs/guide/customization/promote-customization.adoc b/dist/docs/guide/customization/promote-customization.adoc index 4b243c35e..2687ac7cc 100644 --- a/dist/docs/guide/customization/promote-customization.adoc +++ b/dist/docs/guide/customization/promote-customization.adoc @@ -1,31 +1,34 @@ ### Promoting custom artifacts -To make the changes available to the servers, they need to be made available in a custom channel. This process is called *promoting artifacts*. Promoted artifacts can come from a different channel (eg. promoting artifacts between STAGE and PROD channels) or from an artifact bundle. +To make the custom artifact available to registered servers, the update artifacts from the customization bundle have to be added to the existing repository. -If the custom channel is empty, the first promotion will populate it with new artifacts. If the channel already contains artifacts, it will update it - adding new artifacts if they're missing and updating to newer versions if they already exist. +The updated artifacts are distributed in a `maven-repository` folder inside the customization archive. If the server is subscribed to a local filesystem customization repository, all the artifacts can be simply copied over. -Promotion is a two-step process: - -. publish artifacts which the channel references in a Maven repository (or make sure they can be consumed from a public Maven repository), -. publish a channel file (or a new version of the channel file) in a Maven repository. - -The repository (or repositories) containing the artifacts and the channel file needs to be accessible when provisioning or updating server installations. The latest available version of the channel file is used when provisioning or updating a server installation. - -#### Promote artifact bundle -*Single server channel* - -If the changes need to be applied only to a single server, a simplified command can be used: +[source, bash] ``` -prospero channel promote --archive= --dir= +$ ./prospero.sh channel list +# customizations + manifest: org.wildfly.prospero:customizations + repositories: + id: custom-repo-0 + url: file:/home/wildfly/custom/repository # <1> + +$ unzip customization-1.0.1.zip # <2> +Archive: customization-1.0.1.zip + creating: customization/ + inflating: artifact-list.yaml + creating: customization/maven-repository/ + [...] + +$ cp -r customization/maven-repositry/* /home/wildfly/custom/repository/ #<3> + +$ ./prospero.sh updates perform #<4> ``` -The artifacts will be added to an internal installation repository and a new version of channel file will be created. +<1> Location of existing customization repository +<2> Extracting updated repository +<3> Adding updated content to existing repository +<4> Apply updated customizations -*Shared channel* - -When a channel is shared between multiple servers, the promote command has to include the URL of the repository and the channel information. -``` -prospero channel promote --archive= --channel-name= --repository-url= -``` -If the repository contains a channel file already, it will be updated with promoted artifacts, and it's version will be incremented before being redeployed into the repository. +If a repository manager like Nexus is used to host the customization repository, the artifacts should be deployed into it. -NOTE: Currently, this operation requires filesystem access to the repository. \ No newline at end of file +WARNING: When promoting different versions of the same customization bundle, older versions of the bundle should never be promoted after a newer version is promoted. The customization repository contains `maven-metadata.xml` files associated with the customization's manifest that would be overwritten by such operation. \ No newline at end of file diff --git a/dist/docs/guide/customization/registering-channel.adoc b/dist/docs/guide/customization/registering-channel.adoc index a23122222..b82c6e180 100644 --- a/dist/docs/guide/customization/registering-channel.adoc +++ b/dist/docs/guide/customization/registering-channel.adoc @@ -7,21 +7,24 @@ The channel can be hosted either in an internal installation repository, serving *Single server channel* ``` -prospero channel initialize --dir= +prospero channel add \ + --channel-name customizations \ + --repositories file:/path/to/local/repository \ + --manifest org.wildfly.prospero:customizations ``` -A server can use a filesystem repository and auto-generated channel to provide customizations. This command will create a Maven repository in `/.installation/` folder and generate empty customization channel. Both the repository and channel will be then added to the server's configuration. This channel should only be used with a single server. +A server can use a filesystem repository and auto-generated channel to provide customizations. This command will register the server to recieve updates from a Maven repository in `/path/to/local/repository` folder. +The customized artifacts should be listed in a manifest file deployed to the above repository as `org.wildfly.prospero:customizations:manifest:yaml:` *Shared channel* +An online repository can be used to distribute the customizations among multiple servers. Each server has to be subscribed to the repository using: + ``` -prospero channel initialize --dir= --repository-url= --channel-name= +prospero channel add \ + --channel-name customizations \ + --repositories http://repository.host/path \ + --manifest=org.wildfly.prospero:customizations ``` -If a shared repository - either filesystem or web-based - is available, it can be used for customizations. This command will add the provided repository and channel-name to the server configuration. - -NOTE: If using the channel across multiple servers, the chanel-name has to be provided by the user, as otherwise random name will be chosen. - -#### Manual configuration -While `prospero channel initialize` supports common use cases, in more complicated scenarios a combination of `prospero channel add|remove|list` and `prospero repo add|remove|list` can be used to set up channels and repositories. For example, if the custom channel is hosted in a repository that the server is already subscribed to, server can be subscribed to using: -`prospero channel add --dir= ` \ No newline at end of file +NOTE: The repository can be used to host customizations applicable to a different group of servers. In that case each set of customizations would have a separate manifest deployed under unique Maven `groupId:artifactId` coordinates. \ No newline at end of file diff --git a/dist/docs/guide/index.adoc b/dist/docs/guide/index.adoc index 463449ce0..10b32dde4 100644 --- a/dist/docs/guide/index.adoc +++ b/dist/docs/guide/index.adoc @@ -1,3 +1,17 @@ = Prospero documentation +:toc: +:sectnums: + +include::overview/index.adoc[] + +include::concepts/index.adoc[] + +include::process/index.adoc[] + +include::metadata/index.adoc[] + +include::usage/index.adoc[] include::customization/index.adoc[] + +include::channels/index.adoc[] diff --git a/dist/docs/guide/metadata/index.adoc b/dist/docs/guide/metadata/index.adoc new file mode 100644 index 000000000..213b4ed0d --- /dev/null +++ b/dist/docs/guide/metadata/index.adoc @@ -0,0 +1,18 @@ +## Prospero server metadata + +In order for Prospero to be able to update a server, the server installation has to contain some additional metadata files. Those files contain information about server's current state and the subscribed channels. The metadata is generated when the server is installed and can be modified using Prospero commands. + +The metadata is split between `/.galleon` and `/.installation` folders. + +[cols="2*"] +|=== +| File | Description +a| `/.installation/installer-channels.yaml` | List of channels the server is subscribed to receive updates from +a| `/.installation/manifest.yaml` | Record of current versions of components installed in the server +a| `/.installation/manifest_version.yaml` | Record of channel manifests used to provision current server. +a| `/.galleon/provisioning.xml` `/.galleon/provisioned.xml` | Gallon files recording provisioned feature-packs and their customizations. +|=== + +### Prospero update history + +The files in the `.installation/` and `.galleon/` folders should only be modified via the prospero tool, not manually. All metadata changes performed by prospero tool are internally versioned and can be later reviewed or rolled back by using the `prospero history` and `prospero revert` commands. \ No newline at end of file diff --git a/dist/docs/guide/overview/index.adoc b/dist/docs/guide/overview/index.adoc new file mode 100644 index 000000000..9b3a92cca --- /dev/null +++ b/dist/docs/guide/overview/index.adoc @@ -0,0 +1,17 @@ +## Overview + +Prospero is a tool designed to install and manage updates of Wildfly servers. + +The tool supports: + + * Provisioning Wildfly servers + * Updating Wildfly servers + * Reverting updates to Wildfly servers + * Tracking updates history + * Management of update sources + +Prospero includes a CLI client and a programmatic API. + +Releases of the command line tool will be available at https://github.com/wildfly-extras/prospero/releases/. + +To use the tool, download and unzip the release zip, then add the bin directory to your system path. Use `prospero.sh` or `prospero.bat` to launch the tool. \ No newline at end of file diff --git a/dist/docs/guide/process/index.adoc b/dist/docs/guide/process/index.adoc new file mode 100644 index 000000000..068a293fd --- /dev/null +++ b/dist/docs/guide/process/index.adoc @@ -0,0 +1,49 @@ +## General provisioning process + +### Installation process + +Prospero delegates provisioning server to Galleon. When Galleon attempts to resolve components needed by the server, instead of using versions provided by the feature packs, Prospero intercepts that request and uses Wildfly Channels to find the correct version of the component and resolve it from the channels repositories. + +image::overview.png[] + +Every time a server is provisioned, it will receive the latest available components. + +### Update process [[update_process]] + +When new versions of components are made available, Prospero can apply those to existing servers. In order to find updates following steps are executed: + +1. Download the latest version of channel manifests from Maven repositories +2. Compare component versions in the server to the latest available versions in channels. + +If updates are available, they will be applied to the server in a following process: + +1. Provision a new, updated server in a temporary directory. +2. Compare the changes between the new and old server. +3. Apply changes in the new server to the base server. + +#### Resolving update conflicts + +Generally user modified files in the server will never be changed during the update. + +Sometimes server files might be modified both in the base server and in an incoming update. An example of this might be a configuration file changed by the user to support their setup and changed in the update to modify a default configuration value. + +In such case, the user changes are persisted and not modified. The new, updated file configuration is created next to the old file with a `.glnew` suffix, and the user is notified that there was a conflict. The user can resolve the conflict manually after the update. + +The exception to this rule are files considered to be `system paths`. They should not be modified by the user and will always be kept in sync with the changes provided by the updates. Any user changes will be replaced during the update and stored with a `.glold` suffix. An example of such files is a content of `modules/system` folder. + +[cols="1,1,1,1"] +|=== +| User modified | Update modified | System path | Resolution +| Y | N | n/a | Original file is used. +| N | Y | n/a | Updated version is used. Original file is discarded +| Y | Y | Y | Updated version is used. Original file is persisted with `.glold` suffix +| Y | Y| N | Original file is used. Updated version is persisted with `.glnew` suffix +|=== + +#### Update operation limitations + +During "*apply changes*" phase of updates, any processes using server's resources need to be stopped to avoid resource locking issues. The user should manually shut down the server before performing update. + +To reduce server downtime, the update operation can be performed in two steps - `prepare` and `apply`. In the first step, a candidate update server is provisioned in a new directory. The second step, compares the changes between the candidate and the base server and applies them to the base server. + +If the update is split into separate steps, the `prepare` step can be performed while the server is running. See <> for details. diff --git a/dist/docs/guide/process/overview.png b/dist/docs/guide/process/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..9733e1b68db08116beb9962b38b88ec6a88aa5dc GIT binary patch literal 33448 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 V#;kF|pbdeLl~k0-z4z$V{{u=v?`8l1 literal 0 HcmV?d00001 diff --git a/dist/docs/guide/usage/channel_mgmt.adoc b/dist/docs/guide/usage/channel_mgmt.adoc new file mode 100644 index 000000000..8f46b4704 --- /dev/null +++ b/dist/docs/guide/usage/channel_mgmt.adoc @@ -0,0 +1,54 @@ +### Channel management + +The server subscribes to the channels to receive updates from them. The list of subscriptions can be managed using `prospero channel` subcommands. + +#### Listing channels + +Command `prospero channel list` prints a summary of subscribed channels. + +[bash, source] +---- +$ ./prospero.sh channel list --dir wfly-27 +Server /tmp/wfly-27 is subscribed to following channels: + +------- +# channel-0 + manifest: org.wildfly.channel:wfly-27 + repositories: + id: central + url: https://repo1.maven.org/maven2/ +------- +---- + +#### Subscribing to a channel + +Command `prospero channel add` subscribes the server to receive updates from additional channels. Each channel has to have a unique name, a manifest and a list of repositories. The channel's artifacts will be used to resolve artifacts during next `update`. + +[bash, source] +---- +$ ./prospero.sh channel add \ + --channel-name dev-channel \ + --manifest org.prospero.channel:wfly-27-dev \ # <1> + --repositories file:/tmp/dev-repository \ # <2> + --dir wfly-27 +Subscribing /tmp/wfly-27 to channel dev-channel + +Channel 'dev-channel' added. +---- +<1> The Maven coordinates (groupId:artifactId) of the new channel's manifest +<2> The Maven repository where the manifest and artifacts can be found in + +#### Unsubscribing from a channel + +Command `channel remove` unsubscribes the server from a selected channel, stopping it from receiving updates from that channel. The artifacts previously installed from that channel will be replaced with artifacts available in remaining channel during next `update` operation. + +[bash, source] +---- +$ ./prospero.sh channel remove \ + --channel-name dev-channel \ + --dir installation-dir +Unsubscribing /tmp/wfly-27 from channel dev-channel + +Channel 'dev-channel' removed. +---- + diff --git a/dist/docs/guide/usage/clone.adoc b/dist/docs/guide/usage/clone.adoc new file mode 100644 index 000000000..c07fc097e --- /dev/null +++ b/dist/docs/guide/usage/clone.adoc @@ -0,0 +1,62 @@ +### Replicating installation + +Sometimes it might be needed to provision a copy of the server, with the same components as the original, in a different environment. To achieve that, Prospero requires following information: + +* a list of component versions used to provision the server, +* a list of channels used during provisioning, +* a list of Galleon feature packs used. + +This information can be obtained from any provisioned server using `clone export` command: + +[source, bash] +---- +$ ./prospero.sh clone export --dir wfly-27 --path snapshot.zip +Exporting /tmp/wfly-27 installation details to /tmp/snapshot.zip +Export complete +---- + +The generated archive contains following files: +[source, bash] +---- +Archive: snapshot.zip + Length Date Time Name +--------- ---------- ----- ---- + 68301 05-26-2023 15:59 manifest.yaml + 476 05-26-2023 15:59 installer-channels.yaml + 606 05-26-2023 15:59 provisioning.xml +--------- ------- + 69383 3 files +---- + +The archive can be used to rebuild the server: +[source,bash] +---- +$ ./prospero clone recreate --dir cloned --path export.zip +Recreating a server in /tmp/cloned based on /tmp/export.zip + +Provisioning configuration: + * org.wildfly:wildfly-galleon-pack:zip +Subscribed channels: +# wildfly + manifest: org.wildfly.channel:wfly-27 + repositories: + id: central + url: https://repo1.maven.org/maven2/ + id: jboss-public + url: https://repository.jboss.org/nexus/content/groups/public/ + id: mrrc + url: https://maven.repository.redhat.com/ga/ + id: local-repo + url: file:/Users/spyrkob/workspaces/set/prospero/prospero/test-repo + +Feature-packs resolved. +Packages installed. +Downloaded artifacts. +JBoss modules installed. +Configurations generated. + +Server installation was restored. +Operation completed in 28.12 seconds. +---- + +NOTE: The user changes made to the original server are not applied to the clone. \ No newline at end of file diff --git a/dist/docs/guide/usage/history.adoc b/dist/docs/guide/usage/history.adoc new file mode 100644 index 000000000..bde1d198a --- /dev/null +++ b/dist/docs/guide/usage/history.adoc @@ -0,0 +1,102 @@ +### Update history + +Each time the server is updated, the previous state of the server is recorded. That state includes the channels used to perform the update and the versions of artifacts used to build the server. + +This information is used to restore the server back to past state. + +#### Viewing history + +Command `history` displays a list of all server updates: + +[source, base] +---- +$ ./prospero.sh history --dir wfly-27 +[02805caa] 2023-05-25T15:07:19Z - update [org.wildfly.channel:wfly-27::1.0.1] +[c5215591] 2023-05-25T15:05:04Z - install [org.wildfly.channel:wfly-27::1.0.0] +---- + +The server state record contains following information: a hash identifier, date of the change, type of the change (installation, update, configuration change) and a summary of channel manifests used in that update. + +The details of each record can be examined using `--revision` parameter: + +[source, base] +---- +$ ./prospero.sh history --dir wfly-27 --revision 02805caa +Updates: + [Updated artifact] io.undertow:undertow-servlet: 2.2.18.Final ==> 2.2.19.Final + [Updated artifact] io.undertow:undertow-websockets-jsr: 2.2.18.Final ==> 2.2.19.Final + [Updated artifact] io.undertow:undertow-core: 2.2.18.Final ==> 2.2.19.Final +---- + + +#### Reverting updates + +The server can be reverted to any of the recorded states. The `revert` commands operate in similar way to `update`. For example, a following command performs a full revert of a server to an earlier installation state: + +[source, base] +---- +$ ./prospero.sh revert perform --dir wfly-27 --revision c5215591 +Reverting server /tmp/wfly-27 to state c5215591 + +Feature-packs resolved. +Packages installed. +Downloaded artifacts. +JBoss modules installed. +Configurations generated. + +Reverted server prepared, comparing changes +Changes found: + io.undertow:undertow-servlet 2.2.19.Final ==> 2.2.18.Final + io.undertow:undertow-websockets-jsr 2.2.19.Final ==> 2.2.18.Final + io.undertow:undertow-core 2.2.19.Final ==> 2.2.18.Final +Continue with revert [y/N]: y +Applying changes + +Server reverted to state 348b9d9c. +Operation completed in 45.83 seconds. +---- + +The revert process works similar to the update process, but instead of resolving component versions from subscribed channels, the component versions recorded in the requested state will be used. + +NOTE: The required components will be downloaded from the channel repositories the server was subscribed to at the requested state. If the channel repositories are no longer available, or do not provide the required component versions, `--repositories` can be used to overwrite them. + +NOTE: The server has to be switched off for the duration of the `revert perform` operation. + +#### Reverting updates using candidate + +Similarly to `update` operation, the `revert` operation can be split into preparing the candidate and applying the changes. In such case, the server downtime is only required during the latter phase. + +[source, bash] +---- +$ ./prospero.sh revert prepare \ #<1> + --candidate-dir candidate \ + --revision c5215591 \ + --dir wfly-27 +Building revert candidate for /tmp/wfly-27 + +Feature-packs resolved. +Packages installed. +Downloaded artifacts. +JBoss modules installed. +Configurations generated. + +Update candidate generated in /Users/spyrkob/workspaces/set/prospero/prospero/candidate +Operation completed in 22.22 seconds. + +$ ./prospero.sh revert apply \ #<2> + --dir wfly-27 \ + --candidate-dir candidate +Reverting server /tmp/wfly-27 to state ab39b0c6 + +Changes found: + io.undertow:undertow-servlet 2.2.19.Final ==> 2.2.18.Final + io.undertow:undertow-websockets-jsr 2.2.19.Final ==> 2.2.18.Final + io.undertow:undertow-core 2.2.19.Final ==> 2.2.18.Final +Continue with revert [y/N]: y +Applying changes + +Server reverted to state ab39b0c6. +Operation completed in 9.40 seconds. +---- +<1> Prepare a revert candidate in `candidate` folder +<2> Apply prepared `candidate` to a server in `wfly-27` \ No newline at end of file diff --git a/dist/docs/guide/usage/index.adoc b/dist/docs/guide/usage/index.adoc new file mode 100644 index 000000000..144b316b5 --- /dev/null +++ b/dist/docs/guide/usage/index.adoc @@ -0,0 +1,11 @@ +## Usage + +include::install.adoc[] + +include::update.adoc[] + +include::channel_mgmt.adoc[] + +include::history.adoc[] + +include::clone.adoc[] diff --git a/dist/docs/guide/usage/install.adoc b/dist/docs/guide/usage/install.adoc new file mode 100644 index 000000000..1efdfddef --- /dev/null +++ b/dist/docs/guide/usage/install.adoc @@ -0,0 +1,95 @@ +### Installation + +To provision a server, Prospero requires following information: + +* manifest definition +* location of channel's Maven repositories +* server provisioning configuration + +#### Installing predefined server profile + +Prospero provides a set of predefined profiles providing a simple way to install standard servers. + +[source, bash] +---- +$ ./prospero.sh install \ + --dir wfly-27 \ + --profile wildfly \ + --manifest /path/to/wildfly-manifest.yaml +---- + +The profile defines a feature-pack name and required repositories. It can also provide manifest information, if that manifest is published in a Maven repository. + +#### Installing a feature pack + +If a desired server configuration is not provided by one of the profiles, Prospero can provision a feature pack based on its Maven coordinates (groupId:artifactId). The resulting server will have a default configuration provided by that feature pack. + +[source, bash] +---- +$ ./prospero.sh install \ + --dir wfly-27 \ # <1> + --fpl org.wildfly:wildfly-ee-galleon-pack \ #<2> + --manifest /path/to/wildfly-manifest.yaml \ #<3> + --repositories https://repo1.maven.org/maven2/,https://repository.jboss.org/nexus/content/groups/public/ #<4> +---- +<1> directory to place provisioned server in +<2> Maven `:` coordinates of the feature pack to be installed +<3> Path to the manifest file +<4> List of Maven repositories containing components listed in the channel file. + +NOTE: An example Wildfly manifest file is available in examples directory. + +#### Installing a customized feature pack + +Galleon feature packs consist of https://docs.wildfly.org/galleon/#_layers[layers] and https://docs.wildfly.org/galleon/#_feature_pack_packages[packages] that can be excluded or included to generate customized server. + +In order to create a customized installation, the required provisioning configuration need to be described in `provisioning.xml` file. For example, following file adds configuration examples to the generated server. + +[source, xml] +---- + + + + + + + + +---- + +The provisioning definition can then be used to create a server: + +[source, bash] +---- +$ ./prospero.sh install \ + --dir wfly-27 \ + --definition provisioning.xml \ + --manifest /path/to/wildfly-manifest.yaml + --repositories https://repo1.maven.org/maven2/,https://repository.jboss.org/nexus/content/groups/public/ +---- + +#### Using offline or mirrored repositories + +By default, any artifacts required to provision the server, will be resolved using public repositories. If the system on which the installation is performed doesn't have access to the required repositories, alternative repositories need to provided using the `--repositories` argument. + +The specified repositories can either be mirrors of public repositories, or local filesystem copies of the repositories. One way to generate such repository is installing the server on an online system using `--local-cache` argument: + +[source, bash] +---- +$ ./prospero.sh install \ #<1> + --dir wfly-27 \ + --profile wildfly \ + --manifest /path/to/wildfly-manifest.yaml \ + --local-cache /path/to/local_repo + +$ ./prospero.sh install \ #<2> + --dir offline-wfly-27 \ + --profile wildfly \ + --manifest /path/to/wildfly-manifest.yaml \ + --repositories /path/to/local_repo \ + --offline +---- +<1> Generates a repository at `/path/to/local_repo` +<2> Installs the server using content of `/path/to/local_repo` instead of default repositories + +NOTE: When using a : to resolve manifest from a repository, the manifest artifact has to have accompanying Maven metadata (maven-metadata.xml). \ No newline at end of file diff --git a/dist/docs/guide/usage/update.adoc b/dist/docs/guide/usage/update.adoc new file mode 100644 index 000000000..b473370c3 --- /dev/null +++ b/dist/docs/guide/usage/update.adoc @@ -0,0 +1,74 @@ +### Update + +Once the server is installed, it can receive updates published in the channels it is subscribed to. + +Following command can be used to update the server using the subscribed channels: + +[source, bash] +---- +$ ./prospero.sh update perform --dir wfly-27 +---- + +If any updates are available, Prospero will print the update summary and prompt for confirmation to continue. If the updates are accepted, the updated server will be prepared in a way described in <>. + +NOTE: The server has to be stopped during `update perform` operation + +#### Checking updates + +`prospero update list` command can be used to check if any updates are available: + +[source, bash] +---- +$ ./prospero.sh update list --dir wfly-27 +---- + + +#### Working with update candidates + +When using `prospero update perform` command, the server has to be stopped during the whole update process. In order to reduce server downtime, it is possible to generate an update candidate while the server is running and apply it to a stopped server in a separate step. + +[source, bash] +---- +$ ./wfly-27/bin/standalone.sh > /dev/null & # <1> + +$ ./prospero.sh update prepare \ # <2> + --dir wfly-27 \ + --candidate-dir candidate-dir \ + --yes + +$ ./wfly-27/bin/jboss-cli.sh -c "shutdown" # <3> + +$ ./prospero.sh update apply \ # <4> + --dir wfly-27 \ + --candidate-dir candidate-dir +---- +<1> start the server in background +<2> generate the candidate in `candidate-dir` folder +<3> stop the server +<4> merge the updates found in `candidate-dir` into `wfly-27` server + +Update candidate is a temporary server provisioned by prospero. It is generated using the latest updates available in the channels, but does not contain any user modifications of the original server. + +NOTE: The candidate can only be applied to a server it was based on and only if the server has not been updated since the candidate was generated. + +#### Using alternative repository + +If the repositories defined in subscribed channels are not available to the server, alternative repositories can be provided using `--repositories` parameter. Those repositories can point to a local copy of the repository or alternate online location. + +[source, bash] +---- +$ ./prospero.sh update perform \ + --dir wfly-27 \ + --repositories https://proxy.corp.org/maven-central # <1> +---- +<1> The updates will be resolved from *https://proxy.corp.org/maven-central* instead of the default repository. + +The `--repositories` can also be used to resolve artifact from a local copy of the repository: +[source, bash] +---- +$ ./prospero.sh update perform \ + --dir wfly-27 \ + --repositories /path/to/repository +---- + +NOTE: At minimum, the alternative repositories have to provide all the channel manifests that the server is subscribed to and updated artifacts. diff --git a/dist/docs/pom.xml b/dist/docs/pom.xml index 554016e42..562b155bd 100644 --- a/dist/docs/pom.xml +++ b/dist/docs/pom.xml @@ -28,62 +28,47 @@ true + + org.asciidoctor + asciidoctor-maven-plugin + + + process-resources + + process-asciidoc + + + ${basedir}/guide + index.adoc + images + html + book + + ${project.version} + ${ec2-pub-ip-dash} + ${ec2-pub-ip} + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + scripts/assembly-docs.xml + + + + + - - - - release - - - release - - - - - - org.asciidoctor - asciidoctor-maven-plugin - - - process-resources - - process-asciidoc - - - ${basedir}/guide - index.adoc - images - html - book - - ${project.version} - ${ec2-pub-ip-dash} - ${ec2-pub-ip} - - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - package - - single - - - - scripts/assembly-docs.xml - - - - - - - - -