From: David Garcia Date: Mon, 28 Jun 2021 14:50:42 +0000 (+0200) Subject: Feature 10884: Juju 2.9 support X-Git-Tag: release-v11.0-start~44 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=d68e0b4f0bc482d61f2e2a775b899237e15f93e9;hp=20611225b7c1283bc5ce1572b5515e898c6df2b5;p=osm%2Fdevops.git Feature 10884: Juju 2.9 support - Update osm bundles (charmed) - Update Juju version (snap and agent version) (charmed and k8s) - Update to new mongodb charm (charmed and k8s) - Fix ingress issue by adding an ingress_class config - Charmed installer: point to local bundles until all charms are in CharmHub Change-Id: I6ff8328e5db6719bbf9d5d4c9b5490f478b2ace8 Signed-off-by: David Garcia --- diff --git a/installers/charm/bundles/osm-ha/bundle.yaml b/installers/charm/bundles/osm-ha/bundle.yaml index 2d494216..0909160e 100644 --- a/installers/charm/bundles/osm-ha/bundle.yaml +++ b/installers/charm/bundles/osm-ha/bundle.yaml @@ -68,22 +68,17 @@ applications: annotations: gui-x: 0 gui-y: 250 - mongodb-k8s: - charm: "cs:~charmed-osm/mongodb-k8s" - channel: "stable" + mongodb: + charm: ch:mongodb-k8s scale: 3 series: kubernetes storage: - database: 50M - options: - replica-set: rs0 - namespace: osm - enable-sidecar: true + db: 50M annotations: gui-x: 0 gui-y: 0 nbi: - charm: "cs:~charmed-osm/nbi-12" + charm: "cs:~charmed-osm/nbi-13" scale: 3 series: kubernetes options: @@ -94,7 +89,7 @@ applications: gui-x: 0 gui-y: -250 ro: - charm: "cs:~charmed-osm/ro-4" + charm: "cs:~charmed-osm/ro-5" scale: 3 series: kubernetes options: @@ -103,14 +98,14 @@ applications: gui-x: -300 gui-y: 250 ng-ui: - charm: "cs:~charmed-osm/ng-ui-21" + charm: "cs:~charmed-osm/ng-ui-22" scale: 3 series: kubernetes annotations: gui-x: 600 gui-y: 0 lcm: - charm: "cs:~charmed-osm/lcm-8" + charm: "cs:~charmed-osm/lcm-9" scale: 3 series: kubernetes options: @@ -120,7 +115,7 @@ applications: gui-x: -300 gui-y: 0 mon: - charm: "cs:~charmed-osm/mon-5" + charm: "cs:~charmed-osm/mon-6" scale: 1 series: kubernetes options: @@ -131,7 +126,7 @@ applications: gui-x: 300 gui-y: 0 pol: - charm: "cs:~charmed-osm/pol-4" + charm: "cs:~charmed-osm/pol-5" scale: 3 series: kubernetes options: @@ -140,7 +135,7 @@ applications: gui-x: -300 gui-y: 500 pla: - charm: "cs:~charmed-osm/pla-9" + charm: "cs:~charmed-osm/pla-10" scale: 3 series: kubernetes options: @@ -149,7 +144,7 @@ applications: gui-x: 600 gui-y: -250 prometheus: - charm: "cs:~charmed-osm/prometheus-4" + charm: "cs:~charmed-osm/prometheus-5" scale: 1 series: kubernetes storage: @@ -160,14 +155,14 @@ applications: gui-x: 300 gui-y: 250 grafana: - charm: "cs:~charmed-osm/grafana-4" + charm: "cs:~charmed-osm/grafana-5" scale: 3 series: kubernetes annotations: gui-x: 300 gui-y: 500 keystone: - charm: "cs:~charmed-osm/keystone-10" + charm: "cs:~charmed-osm/keystone-11" scale: 1 series: kubernetes annotations: @@ -183,27 +178,27 @@ relations: - - lcm:kafka - kafka-k8s:kafka - - lcm:mongodb - - mongodb-k8s:mongo + - mongodb:database - - ro:ro - lcm:ro - - ro:kafka - kafka-k8s:kafka - - ro:mongodb - - mongodb-k8s:mongo + - mongodb:database - - pol:kafka - kafka-k8s:kafka - - pol:mongodb - - mongodb-k8s:mongo + - mongodb:database - - mon:mongodb - - mongodb-k8s:mongo + - mongodb:database - - mon:kafka - kafka-k8s:kafka - - pla:kafka - kafka-k8s:kafka - - pla:mongodb - - mongodb-k8s:mongo + - mongodb:database - - nbi:mongodb - - mongodb-k8s:mongo + - mongodb:database - - nbi:kafka - kafka-k8s:kafka - - nbi:prometheus diff --git a/installers/charm/bundles/osm/bundle.yaml b/installers/charm/bundles/osm/bundle.yaml index 92e2aca5..bdd4cd63 100644 --- a/installers/charm/bundles/osm/bundle.yaml +++ b/installers/charm/bundles/osm/bundle.yaml @@ -61,22 +61,17 @@ applications: annotations: gui-x: 0 gui-y: 250 - mongodb-k8s: - charm: "cs:~charmed-osm/mongodb-k8s" - channel: "stable" + mongodb: + charm: ch:mongodb-k8s scale: 1 series: kubernetes storage: - database: 50M - options: - replica-set: rs0 - namespace: osm - enable-sidecar: true + db: 50M annotations: gui-x: 0 gui-y: 0 nbi: - charm: "cs:~charmed-osm/nbi-12" + charm: "cs:~charmed-osm/nbi-13" scale: 1 series: kubernetes options: @@ -87,7 +82,7 @@ applications: gui-x: 0 gui-y: -250 ro: - charm: "cs:~charmed-osm/ro-4" + charm: "cs:~charmed-osm/ro-5" scale: 1 series: kubernetes options: @@ -96,14 +91,14 @@ applications: gui-x: -300 gui-y: 250 ng-ui: - charm: "cs:~charmed-osm/ng-ui-21" + charm: "cs:~charmed-osm/ng-ui-22" scale: 1 series: kubernetes annotations: gui-x: 600 gui-y: 0 lcm: - charm: "cs:~charmed-osm/lcm-8" + charm: "cs:~charmed-osm/lcm-9" scale: 1 series: kubernetes options: @@ -113,7 +108,7 @@ applications: gui-x: -300 gui-y: 0 mon: - charm: "cs:~charmed-osm/mon-5" + charm: "cs:~charmed-osm/mon-6" scale: 1 series: kubernetes options: @@ -124,7 +119,7 @@ applications: gui-x: 300 gui-y: 0 pol: - charm: "cs:~charmed-osm/pol-4" + charm: "cs:~charmed-osm/pol-5" scale: 1 series: kubernetes options: @@ -133,7 +128,7 @@ applications: gui-x: -300 gui-y: 500 pla: - charm: "cs:~charmed-osm/pla-9" + charm: "cs:~charmed-osm/pla-10" scale: 1 series: kubernetes options: @@ -142,7 +137,7 @@ applications: gui-x: 600 gui-y: -250 prometheus: - charm: "cs:~charmed-osm/prometheus-4" + charm: "cs:~charmed-osm/prometheus-5" scale: 1 series: kubernetes storage: @@ -153,14 +148,14 @@ applications: gui-x: 300 gui-y: 250 grafana: - charm: "cs:~charmed-osm/grafana-4" + charm: "cs:~charmed-osm/grafana-5" scale: 1 series: kubernetes annotations: gui-x: 300 gui-y: 500 keystone: - charm: "cs:~charmed-osm/keystone-10" + charm: "cs:~charmed-osm/keystone-11" scale: 1 series: kubernetes annotations: @@ -176,27 +171,27 @@ relations: - - lcm:kafka - kafka-k8s:kafka - - lcm:mongodb - - mongodb-k8s:mongo + - mongodb:database - - ro:ro - lcm:ro - - ro:kafka - kafka-k8s:kafka - - ro:mongodb - - mongodb-k8s:mongo + - mongodb:database - - pol:kafka - kafka-k8s:kafka - - pol:mongodb - - mongodb-k8s:mongo + - mongodb:database - - mon:mongodb - - mongodb-k8s:mongo + - mongodb:database - - mon:kafka - kafka-k8s:kafka - - pla:kafka - kafka-k8s:kafka - - pla:mongodb - - mongodb-k8s:mongo + - mongodb:database - - nbi:mongodb - - mongodb-k8s:mongo + - mongodb:database - - nbi:kafka - kafka-k8s:kafka - - nbi:prometheus diff --git a/installers/charm/grafana/config.yaml b/installers/charm/grafana/config.yaml index 19274e53..3a7e63fc 100644 --- a/installers/charm/grafana/config.yaml +++ b/installers/charm/grafana/config.yaml @@ -27,6 +27,11 @@ options: of Keystone, it may need to be configured to handle the requested size. Note: if set to 0, there is no limit. default: 0 + ingress_class: + type: string + description: | + Ingress class name. This is useful for selecting the ingress to be used + in case there are multiple ingresses in the underlying k8s clusters. ingress_whitelist_source_range: type: string description: | diff --git a/installers/charm/grafana/src/charm.py b/installers/charm/grafana/src/charm.py index a7cc036d..28be79aa 100755 --- a/installers/charm/grafana/src/charm.py +++ b/installers/charm/grafana/src/charm.py @@ -51,6 +51,7 @@ class ConfigModel(ModelValidator): osm_dashboards: bool site_url: Optional[str] cluster_issuer: Optional[str] + ingress_class: Optional[str] ingress_whitelist_source_range: Optional[str] tls_secret_name: Optional[str] @@ -174,9 +175,10 @@ class GrafanaCharm(CharmedOsmBase): str(config.max_file_size) + "m" if config.max_file_size > 0 else config.max_file_size - ), - "kubernetes.io/ingress.class": "public", + ) } + if config.ingress_class: + annotations["kubernetes.io/ingress.class"] = config.ingress_class ingress_resource_builder = IngressResourceV3Builder( f"{self.app.name}-ingress", annotations ) diff --git a/installers/charm/kafka-exporter/config.yaml b/installers/charm/kafka-exporter/config.yaml index 8d3703e4..706e330d 100644 --- a/installers/charm/kafka-exporter/config.yaml +++ b/installers/charm/kafka-exporter/config.yaml @@ -20,6 +20,11 @@ ## options: + ingress_class: + type: string + description: | + Ingress class name. This is useful for selecting the ingress to be used + in case there are multiple ingresses in the underlying k8s clusters. ingress_whitelist_source_range: type: string description: | diff --git a/installers/charm/kafka-exporter/src/charm.py b/installers/charm/kafka-exporter/src/charm.py index 3b599a85..a15abc84 100755 --- a/installers/charm/kafka-exporter/src/charm.py +++ b/installers/charm/kafka-exporter/src/charm.py @@ -49,6 +49,7 @@ PORT = 9308 class ConfigModel(ModelValidator): site_url: Optional[str] cluster_issuer: Optional[str] + ingress_class: Optional[str] ingress_whitelist_source_range: Optional[str] tls_secret_name: Optional[str] @@ -194,7 +195,9 @@ class KafkaExporterCharm(CharmedOsmBase): # Add ingress resources to PodSpec if site url exists if config.site_url: parsed = urlparse(config.site_url) - annotations = {"kubernetes.io/ingress.class": "public"} + annotations = {} + if config.ingress_class: + annotations["kubernetes.io/ingress.class"] = config.ingress_class ingress_resource_builder = IngressResourceV3Builder( f"{self.app.name}-ingress", annotations ) diff --git a/installers/charm/keystone/config.yaml b/installers/charm/keystone/config.yaml index 402096ca..9a7acd5e 100644 --- a/installers/charm/keystone/config.yaml +++ b/installers/charm/keystone/config.yaml @@ -20,6 +20,11 @@ options: If there is a reverse proxy in front of Keystone, it may need to be configured to handle the requested size. default: 5 + ingress_class: + type: string + description: | + Ingress class name. This is useful for selecting the ingress to be used + in case there are multiple ingresses in the underlying k8s clusters. ingress_whitelist_source_range: type: string description: | diff --git a/installers/charm/keystone/src/charm.py b/installers/charm/keystone/src/charm.py index 8300b88d..1dd0ba58 100755 --- a/installers/charm/keystone/src/charm.py +++ b/installers/charm/keystone/src/charm.py @@ -78,6 +78,7 @@ class ConfigModel(ModelValidator): token_expiration: int max_file_size: int site_url: Optional[str] + ingress_class: Optional[str] ingress_whitelist_source_range: Optional[str] tls_secret_name: Optional[str] mysql_host: Optional[str] @@ -369,9 +370,10 @@ class KeystoneCharm(CharmedOsmBase): str(config.max_file_size) + "m" if config.max_file_size > 0 else config.max_file_size - ), - "kubernetes.io/ingress.class": "public", + ) } + if config.ingress_class: + annotations["kubernetes.io/ingress.class"] = config.ingress_class ingress_resource_builder = IngressResourceV3Builder( f"{self.app.name}-ingress", annotations ) diff --git a/installers/charm/mongodb-exporter/config.yaml b/installers/charm/mongodb-exporter/config.yaml index 206bca55..727598b9 100644 --- a/installers/charm/mongodb-exporter/config.yaml +++ b/installers/charm/mongodb-exporter/config.yaml @@ -20,6 +20,11 @@ ## options: + ingress_class: + type: string + description: | + Ingress class name. This is useful for selecting the ingress to be used + in case there are multiple ingresses in the underlying k8s clusters. ingress_whitelist_source_range: type: string description: | diff --git a/installers/charm/mongodb-exporter/src/charm.py b/installers/charm/mongodb-exporter/src/charm.py index a2b94719..149940a6 100755 --- a/installers/charm/mongodb-exporter/src/charm.py +++ b/installers/charm/mongodb-exporter/src/charm.py @@ -49,6 +49,7 @@ PORT = 9216 class ConfigModel(ModelValidator): site_url: Optional[str] cluster_issuer: Optional[str] + ingress_class: Optional[str] ingress_whitelist_source_range: Optional[str] tls_secret_name: Optional[str] mongodb_uri: Optional[str] @@ -214,7 +215,9 @@ class MongodbExporterCharm(CharmedOsmBase): # Add ingress resources to PodSpec if site url exists if config.site_url: parsed = urlparse(config.site_url) - annotations = {"kubernetes.io/ingress.class": "public"} + annotations = {} + if config.ingress_class: + annotations["kubernetes.io/ingress.class"] = config.ingress_class ingress_resource_builder = IngressResourceV3Builder( f"{self.app.name}-ingress", annotations ) diff --git a/installers/charm/mysqld-exporter/config.yaml b/installers/charm/mysqld-exporter/config.yaml index f1192a15..a7702a3f 100644 --- a/installers/charm/mysqld-exporter/config.yaml +++ b/installers/charm/mysqld-exporter/config.yaml @@ -20,6 +20,11 @@ ## options: + ingress_class: + type: string + description: | + Ingress class name. This is useful for selecting the ingress to be used + in case there are multiple ingresses in the underlying k8s clusters. ingress_whitelist_source_range: type: string description: | diff --git a/installers/charm/mysqld-exporter/src/charm.py b/installers/charm/mysqld-exporter/src/charm.py index 85a1e67a..bcd43b54 100755 --- a/installers/charm/mysqld-exporter/src/charm.py +++ b/installers/charm/mysqld-exporter/src/charm.py @@ -49,6 +49,7 @@ PORT = 9104 class ConfigModel(ModelValidator): site_url: Optional[str] cluster_issuer: Optional[str] + ingress_class: Optional[str] ingress_whitelist_source_range: Optional[str] tls_secret_name: Optional[str] mysql_uri: Optional[str] @@ -210,7 +211,9 @@ class MysqlExporterCharm(CharmedOsmBase): # Add ingress resources to PodSpec if site url exists if config.site_url: parsed = urlparse(config.site_url) - annotations = {"kubernetes.io/ingress.class": "public"} + annotations = {} + if config.ingress_class: + annotations["kubernetes.io/ingress.class"] = config.ingress_class ingress_resource_builder = IngressResourceV3Builder( f"{self.app.name}-ingress", annotations ) diff --git a/installers/charm/nbi/config.yaml b/installers/charm/nbi/config.yaml index a3d94bc2..7f327527 100644 --- a/installers/charm/nbi/config.yaml +++ b/installers/charm/nbi/config.yaml @@ -27,6 +27,11 @@ options: of Keystone, it may need to be configured to handle the requested size. Note: if set to 0, there is no limit. default: 0 + ingress_class: + type: string + description: | + Ingress class name. This is useful for selecting the ingress to be used + in case there are multiple ingresses in the underlying k8s clusters. ingress_whitelist_source_range: type: string description: | diff --git a/installers/charm/nbi/src/charm.py b/installers/charm/nbi/src/charm.py index 0af41045..d3a22519 100755 --- a/installers/charm/nbi/src/charm.py +++ b/installers/charm/nbi/src/charm.py @@ -57,6 +57,7 @@ class ConfigModel(ModelValidator): max_file_size: int site_url: Optional[str] cluster_issuer: Optional[str] + ingress_class: Optional[str] ingress_whitelist_source_range: Optional[str] tls_secret_name: Optional[str] mongodb_uri: Optional[str] @@ -249,8 +250,9 @@ class NbiCharm(CharmedOsmBase): else config.max_file_size ), "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - "kubernetes.io/ingress.class": "public", } + if config.ingress_class: + annotations["kubernetes.io/ingress.class"] = config.ingress_class ingress_resource_builder = IngressResourceV3Builder( f"{self.app.name}-ingress", annotations ) diff --git a/installers/charm/ng-ui/config.yaml b/installers/charm/ng-ui/config.yaml index df096983..39d3b2d9 100644 --- a/installers/charm/ng-ui/config.yaml +++ b/installers/charm/ng-ui/config.yaml @@ -31,6 +31,11 @@ options: of Keystone, it may need to be configured to handle the requested size. Note: if set to 0, there is no limit. default: 0 + ingress_class: + type: string + description: | + Ingress class name. This is useful for selecting the ingress to be used + in case there are multiple ingresses in the underlying k8s clusters. ingress_whitelist_source_range: type: string description: | diff --git a/installers/charm/ng-ui/src/charm.py b/installers/charm/ng-ui/src/charm.py index 1c53163e..5388466e 100755 --- a/installers/charm/ng-ui/src/charm.py +++ b/installers/charm/ng-ui/src/charm.py @@ -51,6 +51,7 @@ class ConfigModel(ModelValidator): max_file_size: int site_url: Optional[str] cluster_issuer: Optional[str] + ingress_class: Optional[str] ingress_whitelist_source_range: Optional[str] tls_secret_name: Optional[str] @@ -148,9 +149,10 @@ class NgUiCharm(CharmedOsmBase): str(config.max_file_size) + "m" if config.max_file_size > 0 else config.max_file_size - ), - "kubernetes.io/ingress.class": "public", + ) } + if config.ingress_class: + annotations["kubernetes.io/ingress.class"] = config.ingress_class ingress_resource_builder = IngressResourceV3Builder( f"{self.app.name}-ingress", annotations ) diff --git a/installers/charm/prometheus/config.yaml b/installers/charm/prometheus/config.yaml index a5f5e8a1..c1c0fb93 100644 --- a/installers/charm/prometheus/config.yaml +++ b/installers/charm/prometheus/config.yaml @@ -35,6 +35,11 @@ options: of Keystone, it may need to be configured to handle the requested size. Note: if set to 0, there is no limit. default: 0 + ingress_class: + type: string + description: | + Ingress class name. This is useful for selecting the ingress to be used + in case there are multiple ingresses in the underlying k8s clusters. ingress_whitelist_source_range: type: string description: | diff --git a/installers/charm/prometheus/src/charm.py b/installers/charm/prometheus/src/charm.py index cf4f9566..e3e0e424 100755 --- a/installers/charm/prometheus/src/charm.py +++ b/installers/charm/prometheus/src/charm.py @@ -56,6 +56,7 @@ class ConfigModel(ModelValidator): max_file_size: int site_url: Optional[str] cluster_issuer: Optional[str] + ingress_class: Optional[str] ingress_whitelist_source_range: Optional[str] tls_secret_name: Optional[str] enable_web_admin_api: bool @@ -196,9 +197,10 @@ class PrometheusCharm(CharmedOsmBase): str(config.max_file_size) + "m" if config.max_file_size > 0 else config.max_file_size - ), - "kubernetes.io/ingress.class": "public", + ) } + if config.ingress_class: + annotations["kubernetes.io/ingress.class"] = config.ingress_class ingress_resource_builder = IngressResourceV3Builder( f"{self.app.name}-ingress", annotations ) diff --git a/installers/charmed_install.sh b/installers/charmed_install.sh index 770dd6ee..ad530428 100755 --- a/installers/charmed_install.sh +++ b/installers/charmed_install.sh @@ -15,18 +15,25 @@ # set -eux -JUJU_AGENT_VERSION=2.8.9 +JUJU_VERSION=2.9 +JUJU_AGENT_VERSION=2.9.5 K8S_CLOUD_NAME="k8s-cloud" KUBECTL="microk8s.kubectl" -MICROK8S_VERSION=1.19 -OSMCLIENT_VERSION=9.0 +MICROK8S_VERSION=1.20 +OSMCLIENT_VERSION=10.0 IMAGES_OVERLAY_FILE=~/.osm/images-overlay.yaml PATH=/snap/bin:${PATH} MODEL_NAME=osm -OSM_BUNDLE=cs:osm-67 -OSM_HA_BUNDLE=cs:osm-ha-52 +# Latest bundles using old mongodb-k8s +# OSM_BUNDLE=cs:osm-68 +# OSM_HA_BUNDLE=cs:osm-ha-54 +# The charm store does not support referencing charms from CharmHub, +# therefore we will point to the local bundles until we migrate all +# charms to CharmHub. +OSM_BUNDLE=/usr/share/osm-devops/installers/charm/bundles/osm/bundle.yaml +OSM_HA_BUNDLE=/usr/share/osm-devops/installers/charm/bundles/osm-ha/bundle.yaml TAG=testing-daily function check_arguments(){ @@ -71,7 +78,7 @@ function install_snaps(){ export KUBECONFIG=${KUBECFG} KUBEGRP=$(id -g -n) fi - sudo snap install juju --classic --channel=2.8/stable + sudo snap install juju --classic --channel=$JUJU_VERSION/stable } function bootstrap_k8s_lxd(){ @@ -458,7 +465,7 @@ if [ -v ONLY_VCA ]; then hostname=`cat /etc/hostname` echo "Use the following command to register the installed VCA to your OSM:" - echo -e " osm vca-add --endpoints $vca_host:$vca_port \ \n --user $vca_user \ \n --secret $vca_secret \ \n --cacert $vca_cacert \ \n --lxd-cloud lxd-cloud \ \n --lxd-credentials lxd-cloud \ \n --k8s-cloud microk8s \ \n --k8s-credentials microk8s\ \n $hostname-vca" + echo -e " osm vca-add --endpoints $vca_host:$vca_port \\\n --user $vca_user \\\n --secret $vca_secret \\\n --cacert $vca_cacert \\\n --lxd-cloud lxd-cloud \\\n --lxd-credentials lxd-cloud \\\n --k8s-cloud microk8s \\\n --k8s-credentials microk8s\\\n $hostname-vca" else deploy_charmed_osm install_osmclient diff --git a/installers/full_install_osm.sh b/installers/full_install_osm.sh index 0d8d6537..817bb7fc 100755 --- a/installers/full_install_osm.sh +++ b/installers/full_install_osm.sh @@ -446,7 +446,7 @@ function install_docker_compose() { function install_juju() { echo "Installing juju" - sudo snap install juju --classic --channel=2.8/stable + sudo snap install juju --classic --channel=$JUJU_VERSION/stable [[ ":$PATH": != *":/snap/bin:"* ]] && PATH="/snap/bin:${PATH}" [ -n "$INSTALL_NOCACHELXDIMAGES" ] || update_juju_images echo "Finished installation of juju" @@ -901,13 +901,7 @@ function deploy_osm_services() { #deploy charmed services function deploy_charmed_services() { juju add-model $OSM_STACK_NAME $OSM_VCA_K8S_CLOUDNAME - # deploy mongodb charm - namespace=$OSM_STACK_NAME - juju deploy cs:~charmed-osm/mongodb-k8s \ - --config enable-sidecar=true \ - --config replica-set=rs0 \ - --config namespace=$namespace \ - -m $namespace + juju deploy ch:mongodb-k8s -m $OSM_STACK_NAME } function deploy_osm_pla_service() { @@ -1548,7 +1542,8 @@ function parse_docker_registry_url() { DOCKER_REGISTRY_URL=$(echo "$DOCKER_REGISTRY_URL" | awk '{split($1,a,"@"); print a[2]}') } -JUJU_AGENT_VERSION=2.8.6 +JUJU_VERSION=2.9 +JUJU_AGENT_VERSION=2.9.5 UNINSTALL="" DEVELOP="" UPDATE=""