~ibmcharmers/ibm-mobilefirst-server

Owner: kwmonroe
Status: Needs Fixing
Vote: -1 (+2 needed for approval)

CPP?: No
OIL?: No

Migrating from old review queue:

https://bugs.launchpad.net/charms/+bug/1478783


Tests

Substrate Status Results Last Updated
aws RETRY 19 days ago
gce RETRY 19 days ago
lxc RETRY 19 days ago

Voted: -1
petevg wrote 3 months ago
Hello,

Thank you for all of your work on this charm! It looks like the linter errors are fixed, and the charms referenced in the README are promulgated. I cannot approve this charm at this time due to the following issues, however:

* The charm currently writes the worklight-user's password to the juju-log file. This is not consistent with security best practices, as your logs will typically be handled with less care by system operators than more obviously sensitive config files.

* The configure_mobilefirstserverdb function is not idempotent. Since it does not check for failure on the scp calls, but does set a .configured state that will prevent it from running again, it will not leave your charm in a consistent state: those important files will never get copied over if the scp call fails! (It would probably be a better idea to pack those files into this charm, as the function is fragile in other respects -- it doesn't verify that the files are valid, and the paths are tied to a specific db2 version, and will break if an operator upgrades db2.)

I also had a note, which does not prevent approval of the charm:

* The calls in get_db_values_from_interface don't need the fallback to the boolean value of true. That function is only called after those values exist; the "|| true" clause guards against a case that should never happen. You're probably much better off throwing an exception and exiting if any of those values are missing, rather than risking failing silently, and writing "true" into a config file.

Thank you again.

Add Comment

Login to comment/vote on this review.


Policy Checklist

Description Unreviewed Pass Fail

General

Must verify that any software installed or utilized is verified as coming from the intended source.
  • Any software installed from the Ubuntu or CentOS default archives satisfies this due to the apt and yum sources including cryptographic signing information.
  • Third party repositories must be listed as a configuration option that can be overridden by the user and not hard coded in the charm itself.
  • Launchpad PPAs are acceptable as the add-apt-repository command retrieves the keys securely.
  • Other third party repositories are acceptable if the signing key is embedded in the charm.
Must provide a means to protect users from known security vulnerabilities in a way consistent with best practices as defined by either operating system policies or upstream documentation. petevg
Basically, this means there must be instructions on how to apply updates if you use software not from distribution channels.
Must have hooks that are idempotent. petevg
Should be built using charm layers. petevg
Should use Juju Resources to deliver required payloads. petevg

Testing and Quality

charm proof must pass without errors or warnings. petevg
Must include passing unit, functional, or integration tests.
Tests must exercise all relations.
Tests must exercise config.
set-config, unset-config, and re-set must be tested as a minimum
Must not use anything infrastructure-provider specific (i.e. querying EC2 metadata service). petevg
Must be self contained unless the charm is a proxy for an existing cloud service, e.g. ec2-elb charm.
Must not use symlinks. petevg
Bundles must only use promulgated charms, they cannot reference charms in personal namespaces. kos.tsakalozos
Must call Juju hook tools (relation-*, unit-*, config-*, etc) without a hard coded path. petevg
Should include a tests.yaml for all integration tests. kos.tsakalozos

Metadata

Must include a full description of what the software does. petevg
Must include a maintainer email address for a team or individual who will be responsive to contact. petevg
Must include a license. Call the file 'copyright' and make sure all files' licenses are specified clearly. petevg
Must be under a Free license. petevg
Must have a well documented and valid README.md. petevg
Must describe the service. petevg
Must describe how it interacts with other services, if applicable. petevg
Must document the interfaces. petevg
Must show how to deploy the charm. petevg
Must define external dependencies, if applicable. petevg
Should link to a recommend production usage bundle and recommended configuration if this differs from the default.
Should reference and link to upstream documentation and best practices.

Security

Must not run any network services using default passwords. petevg
Must verify and validate any external payload
  • Known and understood packaging systems that verify packages like apt, pip, and yum are ok.
  • wget | sh style is not ok.
Should make use of whatever Mandatory Access Control system is provided by the distribution. petevg
Should avoid running services as root. petevg

All changes | Changes since last revision

Source Diff

Files changed 115

Inline diff comments 0

No comments yet.

Back to file index

Makefile

 1
--- 
 2
+++ Makefile
 3
@@ -0,0 +1,24 @@
 4
+#!/usr/bin/make
 5
+
 6
+all: lint unit_test
 7
+
 8
+
 9
+.PHONY: clean
10
+clean:
11
+	@rm -rf .tox
12
+
13
+.PHONY: apt_prereqs
14
+apt_prereqs:
15
+	@# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip)
16
+	@which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox)
17
+
18
+.PHONY: lint
19
+lint: apt_prereqs
20
+	@tox --notest
21
+	@PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests)
22
+	@charm proof
23
+
24
+.PHONY: unit_test
25
+unit_test: apt_prereqs
26
+	@echo Starting tests...
27
+	tox
Back to file index

README.md

  1
--- 
  2
+++ README.md
  3
@@ -0,0 +1,115 @@
  4
+Charm for IBM MobileFirst Server 7.0
  5
+
  6
+
  7
+Overview
  8
+--------
  9
+This charm installs IBM MobileFirst Server software.
 10
+
 11
+`IBM MobileFirst Server`
 12
+
 13
+IBM MobileFirst Server is mobile-optimized middleware that serves as a gateway between applications, back-end systems and cloud-based services. IBM MobileFirst Platform Foundation provides an open, comprehensive platform to develop, test, secure and manage mobile apps
 14
+ 
 15
+Prerequisites
 16
+-------------
 17
+
 18
+This charm makes use of resources, a feature only available in Juju 2.0. During deploy or upgrade, you will need to specify the installable package(s) required by this charm. Download your licensed IBM MobileFirst Server packages from the [Product Page] [mfs-product-page]. For details on IBM MobileFirst Server refer to [IBM Knowledge Center] [mfs-v7-info]
 19
+
 20
+For Ubuntu (Power and x86_64), IBM MobileFirst Server package and part number is:
 21
+
 22
+          IBM MobileFirst Server 7.0 Installer (CN4V2EN) 
 23
+
 24
+
 25
+Usage
 26
+-------
 27
+To use this charm, you must agree to the Terms of Use. You can view the full license for IBM Mobile First Server by visiting the [LicenseInfo] [license-page].
 28
+
 29
+Memory and Disk Requirements
 30
+-----------------------------
 31
+Minimum 150 MB disk space and 2GB RAM memory is required.
 32
+
 33
+Deploy
 34
+-------
 35
+To deploy ibm-mobilefirst-server charm you need `ibm-db2` and `websphere-liberty` charms.
 36
+
 37
+The MobileFirst Server charm is a sub ordinate charm to IBM WebSphere Liberty charm and hence will be deployed to the same container as IBM WebSphere Liberty software.
 38
+
 39
+Run the following commands to deploy this charm:
 40
+
 41
+Following commands would deploy websphere-liberty charm. Mobile First Server needs Websphere Liberty version 8.5.5.8.
 42
+
 43
+    1. juju deploy websphere-liberty 
 44
+    2. juju config websphere-liberty accept-ibm-websphere-license=True
 45
+    3. juju config websphere-liberty ibm-liberty-version="8.5.5.8"
 46
+
 47
+Please go through the [websphere-liberty] [wlp-readme] charm for detailed deployment instructions. 
 48
+
 49
+Following commands would deploy ibm-db2 charm.
 50
+
 51
+    4. juju deploy ibm-db2 
 52
+    5. juju config ibm-db2 curl_opts="<login credentials>"
 53
+    6. juju config ibm-db2 curl_url="<url to download package?shavalue>"
 54
+    7. juju config ibm-db2 license_accepted="True"
 55
+
 56
+Please go through the [ibm-db2] [db2-readme] charm file for detailed deployment instructions.
 57
+
 58
+Deploy ibm-mobilefirst-server charm using the below steps. Once deployed, ibm-mobilefirst-server has to be related to websphere-liberty and ibm-db2. 
 59
+ibm-mobilefirt-server charm is built on the top of ibm-im layer and hence the user has to provide the IBM-IM resource also while deploying the MobileFirst.
 60
+         
 61
+    8. juju deploy ibm-mobilefirst-server --resource=ibm_im_installer=</path/to/ibm_im_installer.zip> --resource=ibm_mobilefirstserver_installer=<path/to/iibm_mobilefirstserver_installer.zip>
 62
+    9. juju add-relation websphere-liberty ibm-mobilefirst-server
 63
+    10. juju add-relation ibm-db2 ibm-mobilefirst-server
 64
+   
 65
+At this point Installation Manager and MobileFirst Server install will wait for you to accept the License. To install the downloaded binaries you must agree to the IBM license. 
 66
+
 67
+    11.juju agree ibm-im/1
 68
+    12.juju agree ibm-mobilefirstserver/1
 69
+
 70
+Once these Terms are accepted Mobilefirst server software will be installed.   
 71
+
 72
+Post Install tests
 73
+------------------
 74
+After installing IBM MobileFirst server software open appcenter and worklight consoles using below link and replace <public IP> with your container IP address. 
 75
+The http and https port numbers in which Mobile First Server runs can be identified from the open-ports value for the charm. 
 76
+       
 77
+     https://<public IP>:<https port>/worklightconsole
 78
+     http://<public IP>:<http port>/worklightconsole
 79
+
 80
+To login worklight console use the default credentials  `admin/admin123`. This is a configurable value and can be changed by the user.
 81
+
 82
+     https://<public IP>:<https port>/appcenterconsole
 83
+     http://<public IP>:<<http port>/appcenterconsole
 84
+
 85
+To login appcenter console use the same default credentials 'admin/admin123'. This can be changed by the user. 
 86
+
 87
+IBM MobileFirst Server information
 88
+-----------------------------------
 89
+(1) General Information
 90
+Details about MobileFirst Server 7.0 available at [IBM Knowledge Center][mfs-v7-info].
 91
+
 92
+(2) Download Information
 93
+Information on procuring MobileFirst Server product is available at the [Product Page][mfs-product-page]
 94
+and at the [Passport Advantage Site][passport].
 95
+
 96
+
 97
+Contact Information
 98
+-------------------
 99
+For issues with this charm, please contact IBM Juju Support team <jujusupp@us.ibm.com>
100
+ 
101
+<!-- Links -->
102
+
103
+   [im-v1.7-info]: http://www-01.ibm.com/support/knowledgecenter/SSDV2W_1.7.0/com.ibm.cic.agent.ui.doc/helpindex_imic.html?cp=SSDV2W_1.7.0%2F0
104
+
105
+   [im-product-page]: http://www-01.ibm.com/support/docview.wss?uid=swg27025142
106
+
107
+   [mfs-product-page]: http://www.ibm.com/software/products/en/mobilefirstfoundation "MFS Info"
108
+
109
+   [mfs-v7-info]: https://www-01.ibm.com/support/knowledgecenter/SSHS8R_7.0.0/com.ibm.worklight.installconfig.doc/install_config/t_ov_server_install.html
110
+
111
+   [license-page]: http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=&li_formnum=L-BVID-9TCPPM&title=IBM%20MobileFirst%20Platform%20Foundation%20V7.0%20zip%20of%20Installation%20Manager%20Repository%20for%20IBM%20MobileFirst%20Server%20Multiplatform%20English
112
+
113
+   [wlp-readme]: https://jujucharms.com/websphere-liberty/trusty/3
114
+
115
+   [db2-readme]: https://jujucharms.com/ibm-db2/trusty/2
116
+
117
+   [passport]: http://www-01.ibm.com/software/how-to-buy/passportadvantage/
118
+
Back to file index

bin/layer_option

 1
--- 
 2
+++ bin/layer_option
 3
@@ -0,0 +1,24 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+import sys
 7
+sys.path.append('lib')
 8
+
 9
+import argparse
10
+from charms.layer import options
11
+
12
+
13
+parser = argparse.ArgumentParser(description='Access layer options.')
14
+parser.add_argument('section',
15
+                    help='the section, or layer, the option is from')
16
+parser.add_argument('option',
17
+                    help='the option to access')
18
+
19
+args = parser.parse_args()
20
+value = options(args.section).get(args.option, '')
21
+if isinstance(value, bool):
22
+    sys.exit(0 if value else 1)
23
+elif isinstance(value, list):
24
+    for val in value:
25
+        print(val)
26
+else:
27
+    print(value)
Back to file index

config.yaml

 1
--- 
 2
+++ config.yaml
 3
@@ -0,0 +1,11 @@
 4
+"options":
 5
+  "worklight_passwd":
 6
+    "type": "string"
 7
+    "default": "admin123"
 8
+    "description": |
 9
+      This parameter contains the default password for logging into Worklight console
10
+  "worklight_user":
11
+    "type": "string"
12
+    "default": "admin"
13
+    "description": |
14
+      This parameter contains the username for logging into Worklight console
Back to file index

copyright

 1
--- 
 2
+++ copyright
 3
@@ -0,0 +1,13 @@
 4
+Copyright 2016 IBM Corporation
 5
+
 6
+This Charm is licensed under the Apache License, Version 2.0 (the "License");
 7
+you may not use this file except in compliance with the License.
 8
+You may obtain a copy of the License at
 9
+
10
+    http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+Unless required by applicable law or agreed to in writing, software
13
+distributed under the License is distributed on an "AS IS" BASIS,
14
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+See the License for the specific language governing permissions and
16
+limitations under the License.
Back to file index

deps/layer/layer-apt/.gitignore

1
--- 
2
+++ deps/layer/layer-apt/.gitignore
3
@@ -0,0 +1,2 @@
4
+*~
5
+*.pyc
Back to file index

deps/layer/layer-apt/README.md

  1
--- 
  2
+++ deps/layer/layer-apt/README.md
  3
@@ -0,0 +1,168 @@
  4
+# Apt layer
  5
+
  6
+The Apt layer for Juju enables layered charms to more easily deal with
  7
+deb packages and apt sources in a simple and efficient manner. It
  8
+provides consistent configuration for operators, allowing them to
  9
+easily specify custom apt sources and additional debs required for
 10
+their particular installations.
 11
+
 12
+## Configuration
 13
+
 14
+The charm may provide defaults for these service configuration
 15
+(config.yaml) options, and the operator may override them as required.
 16
+
 17
+* `extra_packages`
 18
+
 19
+  A space separated list of additional deb packages to install on
 20
+  each unit.
 21
+
 22
+* `package_status`
 23
+
 24
+  'install' or 'hold'. When set to hold, packages installed using
 25
+  the Apt layer API will be pinned, so that they will not be
 26
+  automatically upgraded when package updates are performed. 'hold'
 27
+  is particularly useful for allowing a service such as Landscape
 28
+  to automatically apply security updates to most of the system,
 29
+  whilst holding back any potentially service affecting updates.
 30
+
 31
+* `install_sources`
 32
+
 33
+  A list of apt sources containing the packages that need to be installed.
 34
+  Each source may be either a line that can be added directly to
 35
+  sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
 36
+  Personal Package Archives, or a distribution component to enable.
 37
+  The list is a yaml list, encoded as a string. The nicest way of
 38
+  declaring this in a yaml file looks like the following (in particular,
 39
+  the | character indicates that the value is a multiline string):
 40
+
 41
+  ```yaml
 42
+  install_sources: |
 43
+      - ppa:stub/cassandra
 44
+      - deb http://www.apache.org/dist/cassandra/debian 21x main
 45
+  ```
 46
+
 47
+* `install_keys`
 48
+
 49
+  A list of GPG signing keys to accept. There needs to be one entry
 50
+  per entry in install_sources. null may be used if no keep is
 51
+  needed, which is the case for PPAs and for the standard Ubuntu
 52
+  archives. Keys should be full ASCII armoured GPG public keys.
 53
+  GPG key ids are also accepted, but in most environments this
 54
+  mechanism is not secure. The install_keys list, like
 55
+  install_sources, must also be a yaml formatted list encoded as
 56
+  a string:
 57
+
 58
+  ```yaml
 59
+  install_keys: |
 60
+      - null
 61
+      - |
 62
+          -----BEGIN PGP PUBLIC KEY BLOCK-----
 63
+          Version: GnuPG v1
 64
+
 65
+          mQINBFQJvgUBEAC0KcYCTj0hd15p4fiXBsbob0sKgsvN5Lm7N9jzJWlGshJ0peMi
 66
+          kH8YhDXw5Lh+mPEHksL7t1L8CIr1a+ntns/Opt65ZPO38ENVkOqEVAn9Z5sIoZsb
 67
+          AUeLlJzSeRLTKhcOugK7UcsQD2FHnMBJz50bxis9X7pjmnc/tWpjAGJfaWdjDIo=
 68
+          =yiQ4
 69
+          -----END PGP PUBLIC KEY BLOCK-----
 70
+  ```
 71
+
 72
+## Usage
 73
+
 74
+Queue packages for installation, and have handlers waiting for
 75
+these packages to finish being installed:
 76
+
 77
+```python
 78
+import charms.apt
 79
+
 80
+@hook('install')
 81
+def install():
 82
+    charms.apt.queue_install(['git'])
 83
+
 84
+@when_not('apt.installed.gnupg')
 85
+def install_gnupg():
 86
+    charms.apt.queue_install(['gnupg'])
 87
+
 88
+@when('apt.installed.git')
 89
+@when('apt.installed.gnupg')
 90
+def grabit():
 91
+    clone_repo()
 92
+    validate_repo()
 93
+```
 94
+
 95
+### API
 96
+
 97
+Several methods are exposed in the charms.apt Python package.
 98
+
 99
+* `add_source(source, key=None)`
100
+
101
+  Add an apt source.
102
+
103
+  A source may be either a line that can be added directly to
104
+  sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
105
+  Personal Package Archives, or a distribution component to enable.
106
+
107
+  The package signing key should be an ASCII armoured GPG key. While
108
+  GPG key ids are also supported, the retrieval mechanism is insecure.
109
+  There is no need to specify the package signing key for PPAs or for
110
+  the main Ubuntu archives.
111
+
112
+  It is preferable if charms do not call this directly to hard
113
+  coded apt sources, but instead have these sources listed
114
+  as defaults in the install_sources config option. This allows
115
+  operators to mirror your packages to internal archives and
116
+  deploy your charm in environments without network access.
117
+
118
+  Sets the `apt.needs_update` reactive state.
119
+
120
+* `queue_install(packages, options=None)`
121
+
122
+  Queue one or more deb packages for install. The actual package
123
+  installation will be performed later by a handler in the
124
+  apt layer. The `apt.installed.{name}` state will be set once
125
+  the package installed (one state for each package).
126
+
127
+  If a package has already been installed it will not be reinstalled.
128
+
129
+  If a package has already been queued it will not be requeued, and
130
+  the install options will not be changed.
131
+
132
+* `installed()`
133
+
134
+  Returns the set of deb packages installed by this layer.
135
+
136
+* `purge(packages)`
137
+
138
+  Purge one or more deb packages from the system
139
+
140
+
141
+### Extras
142
+
143
+These methods are called automatically by the reactive framework as
144
+reactive state demands. However, you can also invoke them directly
145
+if you want the operation done right now.
146
+
147
+* `update()`
148
+
149
+  Update the apt cache. Removes the `apt.needs_update` state.
150
+
151
+
152
+* `install_queued()`
153
+
154
+  Installs deb packages queued for installation. On success, removes
155
+  the `apt.queued_installs` state, sets the `apt.installed.{packagename}`
156
+  state for each installed package, and returns True. On failure,
157
+  sets the unit workload status to blocked and returns False.
158
+  The package installs remain queued.
159
+
160
+
161
+## Support
162
+
163
+This layer is maintained on Launchpad by
164
+Stuart Bishop (stuart.bishop@canonical.com).
165
+
166
+Code is available using git at git+ssh://git.launchpad.net/layer-apt.
167
+
168
+Bug reports can be made at https://bugs.launchpad.net/layer-apt.
169
+
170
+Queries and comments can be made on the Juju mailing list, Juju IRC
171
+channels, or at https://answers.launchpad.net/layer-apt.
Back to file index

deps/layer/layer-apt/config.yaml

 1
--- 
 2
+++ deps/layer/layer-apt/config.yaml
 3
@@ -0,0 +1,34 @@
 4
+options:
 5
+  extra_packages:
 6
+    description: >
 7
+        Space separated list of extra deb packages to install.
 8
+    type: string
 9
+    default: ""
10
+  package_status:
11
+    default: "install"
12
+    type: string
13
+    description: >
14
+        The status of service-affecting packages will be set to this
15
+        value in the dpkg database. Valid values are "install" and "hold".
16
+  install_sources:
17
+    description: >
18
+        List of extra apt sources, per charm-helpers standard
19
+        format (a yaml list of strings encoded as a string). Each source
20
+        may be either a line that can be added directly to
21
+        sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
22
+        Personal Package Archives, or a distribution component to enable.
23
+    type: string
24
+    default: ""
25
+  install_keys:
26
+    description: >
27
+        List of signing keys for install_sources package sources, per
28
+        charmhelpers standard format (a yaml list of strings encoded as
29
+        a string). The keys should be the full ASCII armoured GPG public
30
+        keys. While GPG key ids are also supported and looked up on a
31
+        keyserver, operators should be aware that this mechanism is
32
+        insecure. null can be used if a standard package signing key is
33
+        used that will already be installed on the machine, and for PPA
34
+        sources where the package signing key is securely retrieved from
35
+        Launchpad.
36
+    type: string
37
+    default: ""
Back to file index

deps/layer/layer-apt/copyright

 1
--- 
 2
+++ deps/layer/layer-apt/copyright
 3
@@ -0,0 +1,15 @@
 4
+Copyright 2015-2016 Canonical Ltd.
 5
+
 6
+This file is part of the Apt layer for Juju.
 7
+
 8
+This program is free software: you can redistribute it and/or modify
 9
+it under the terms of the GNU General Public License version 3, as
10
+published by the Free Software Foundation.
11
+
12
+This program is distributed in the hope that it will be useful, but
13
+WITHOUT ANY WARRANTY; without even the implied warranties of
14
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+PURPOSE.  See the GNU General Public License for more details.
16
+
17
+You should have received a copy of the GNU General Public License
18
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
Back to file index

deps/layer/layer-apt/lib/charms/__init__.py

1
--- 
2
+++ deps/layer/layer-apt/lib/charms/__init__.py
3
@@ -0,0 +1,2 @@
4
+from pkgutil import extend_path
5
+__path__ = extend_path(__path__, __name__)
Back to file index

deps/layer/layer-apt/lib/charms/apt.py

  1
--- 
  2
+++ deps/layer/layer-apt/lib/charms/apt.py
  3
@@ -0,0 +1,180 @@
  4
+# Copyright 2015-2016 Canonical Ltd.
  5
+#
  6
+# This file is part of the Apt layer for Juju.
  7
+#
  8
+# This program is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU General Public License version 3, as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# This program is distributed in the hope that it will be useful, but
 13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
 14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 15
+# PURPOSE.  See the GNU General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU General Public License
 18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+'''
 21
+charms.reactive helpers for dealing with deb packages.
 22
+
 23
+Add apt package sources using add_source(). Queue deb packages for
 24
+installation with install(). Configure and work with your software
 25
+once the apt.installed.{packagename} state is set.
 26
+'''
 27
+import itertools
 28
+import subprocess
 29
+
 30
+from charmhelpers import fetch
 31
+from charmhelpers.core import hookenv, unitdata
 32
+from charms import reactive
 33
+
 34
+
 35
+__all__ = ['add_source', 'update', 'queue_install', 'install_queued',
 36
+           'installed', 'purge', 'ensure_package_status']
 37
+
 38
+
 39
+def add_source(source, key=None):
 40
+    '''Add an apt source.
 41
+
 42
+    Sets the apt.needs_update state.
 43
+
 44
+    A source may be either a line that can be added directly to
 45
+    sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
 46
+    Personal Package Archives, or a distribution component to enable.
 47
+
 48
+    The package signing key should be an ASCII armoured GPG key. While
 49
+    GPG key ids are also supported, the retrieval mechanism is insecure.
 50
+    There is no need to specify the package signing key for PPAs or for
 51
+    the main Ubuntu archives.
 52
+    '''
 53
+    # Maybe we should remember which sources have been added already
 54
+    # so we don't waste time re-adding them. Is this time significant?
 55
+    fetch.add_source(source, key)
 56
+    reactive.set_state('apt.needs_update')
 57
+
 58
+
 59
+def queue_install(packages, options=None):
 60
+    """Queue one or more deb packages for install.
 61
+
 62
+    The `apt.installed.{name}` state is set once the package is installed.
 63
+
 64
+    If a package has already been installed it will not be reinstalled.
 65
+
 66
+    If a package has already been queued it will not be requeued, and
 67
+    the install options will not be changed.
 68
+
 69
+    Sets the apt.queued_installs state.
 70
+    """
 71
+    # Filter installed packages.
 72
+    store = unitdata.kv()
 73
+    queued_packages = store.getrange('apt.install_queue.', strip=True)
 74
+    packages = {package: options for package in packages
 75
+                if not (package in queued_packages or
 76
+                        reactive.helpers.is_state('apt.installed.' + package))}
 77
+    if packages:
 78
+        unitdata.kv().update(packages, prefix='apt.install_queue.')
 79
+        reactive.set_state('apt.queued_installs')
 80
+
 81
+
 82
+def installed():
 83
+    '''Return the set of deb packages completed install'''
 84
+    return set(state.split('.', 2)[2] for state in reactive.bus.get_states()
 85
+               if state.startswith('apt.installed.'))
 86
+
 87
+
 88
+def purge(packages):
 89
+    """Purge one or more deb packages from the system"""
 90
+    fetch.apt_purge(packages, fatal=True)
 91
+    store = unitdata.kv()
 92
+    store.unsetrange(packages, prefix='apt.install_queue.')
 93
+    for package in packages:
 94
+        reactive.remove_state('apt.installed.{}'.format(package))
 95
+
 96
+
 97
+def update():
 98
+    """Update the apt cache.
 99
+
100
+    Removes the apt.needs_update state.
101
+    """
102
+    status_set(None, 'Updating apt cache')
103
+    fetch.apt_update(fatal=True)  # Friends don't let friends set fatal=False
104
+    reactive.remove_state('apt.needs_update')
105
+
106
+
107
+def install_queued():
108
+    '''Installs queued deb packages.
109
+
110
+    Removes the apt.queued_installs state and sets the apt.installed state.
111
+
112
+    On failure, sets the unit's workload state to 'blocked' and returns
113
+    False. Package installs remain queued.
114
+
115
+    On success, sets the apt.installed.{packagename} state for each
116
+    installed package and returns True.
117
+    '''
118
+    store = unitdata.kv()
119
+    queue = sorted((options, package)
120
+                   for package, options in store.getrange('apt.install_queue.',
121
+                                                          strip=True).items())
122
+
123
+    installed = set()
124
+    for options, batch in itertools.groupby(queue, lambda x: x[0]):
125
+        packages = [b[1] for b in batch]
126
+        try:
127
+            status_set(None, 'Installing {}'.format(','.join(packages)))
128
+            fetch.apt_install(packages, options, fatal=True)
129
+            store.unsetrange(packages, prefix='apt.install_queue.')
130
+            installed.update(packages)
131
+        except subprocess.CalledProcessError:
132
+            status_set('blocked',
133
+                       'Unable to install packages {}'
134
+                       .format(','.join(packages)))
135
+            return False  # Without setting reactive state.
136
+
137
+    for package in installed:
138
+        reactive.set_state('apt.installed.{}'.format(package))
139
+
140
+    reactive.remove_state('apt.queued_installs')
141
+    return True
142
+
143
+
144
+def ensure_package_status():
145
+    '''Hold or unhold packages per the package_status configuration option.
146
+
147
+    All packages installed using this module and handlers are affected.
148
+
149
+    An mechanism may be added in the future to override this for a
150
+    subset of installed packages.
151
+    '''
152
+    packages = installed()
153
+    if not packages:
154
+        return
155
+    config = hookenv.config()
156
+    package_status = config['package_status']
157
+    changed = reactive.helpers.data_changed('apt.package_status',
158
+                                            (package_status, sorted(packages)))
159
+    if changed:
160
+        if package_status == 'hold':
161
+            hookenv.log('Holding packages {}'.format(','.join(packages)))
162
+            fetch.apt_hold(packages)
163
+        else:
164
+            hookenv.log('Unholding packages {}'.format(','.join(packages)))
165
+            fetch.apt_unhold(packages)
166
+    reactive.remove_state('apt.needs_hold')
167
+
168
+
169
+def status_set(state, message):
170
+    """Set the unit's workload status.
171
+
172
+    Set state == None to keep the same state and just change the message.
173
+    """
174
+    if state is None:
175
+        state = hookenv.status_get()[0]
176
+        if state == 'unknown':
177
+            state = 'maintenance'  # Guess
178
+    if state in ('error', 'blocked'):
179
+        lvl = hookenv.WARNING
180
+    else:
181
+        lvl = hookenv.INFO
182
+    hookenv.status_set(state, message)
183
+    hookenv.log('{}: {}'.format(state, message), lvl)
Back to file index

deps/layer/layer-apt/reactive/apt.py

  1
--- 
  2
+++ deps/layer/layer-apt/reactive/apt.py
  3
@@ -0,0 +1,98 @@
  4
+# Copyright 2015-2016 Canonical Ltd.
  5
+#
  6
+# This file is part of the Apt layer for Juju.
  7
+#
  8
+# This program is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU General Public License version 3, as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# This program is distributed in the hope that it will be useful, but
 13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
 14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 15
+# PURPOSE.  See the GNU General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU General Public License
 18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+'''
 21
+charms.reactive helpers for dealing with deb packages.
 22
+
 23
+Add apt package sources using add_source(). Queue deb packages for
 24
+installation with install(). Configure and work with your software
 25
+once the apt.installed.{packagename} state is set.
 26
+'''
 27
+from charmhelpers import fetch
 28
+from charmhelpers.core import hookenv
 29
+from charms import reactive
 30
+from charms.reactive import when, when_not
 31
+
 32
+import charms.apt
 33
+# Aliases for backwards compatibility
 34
+from charms.apt import add_source, queue_install, installed, purge
 35
+
 36
+
 37
+__all__ = ['add_source', 'update', 'queue_install', 'install_queued',
 38
+           'installed', 'purge', 'ensure_package_status']
 39
+
 40
+
 41
+@when('apt.needs_update')
 42
+def update():
 43
+    charms.apt.update()
 44
+
 45
+
 46
+@when('apt.queued_installs')
 47
+@when_not('apt.needs_update')
 48
+def install_queued():
 49
+    charms.apt.install_queued()
 50
+
 51
+
 52
+@when_not('apt.queued_installs')
 53
+def ensure_package_status():
 54
+    charms.apt.ensure_package_status()
 55
+
 56
+
 57
+def configure_sources():
 58
+    """Add user specified package sources from the service configuration.
 59
+
 60
+    See charmhelpers.fetch.configure_sources for details.
 61
+    """
 62
+    hookenv.log('Initializing Apt Layer')
 63
+    config = hookenv.config()
 64
+
 65
+    # We don't have enums, so we need to validate this ourselves.
 66
+    package_status = config.get('package_status')
 67
+    if package_status not in ('hold', 'install'):
 68
+        charms.apt.status_set('blocked',
 69
+                              'Unknown package_status {}'
 70
+                              ''.format(package_status))
 71
+        # Die before further hooks are run. This isn't very nice, but
 72
+        # there is no other way to inform the operator that they have
 73
+        # invalid configuration.
 74
+        raise SystemExit(0)
 75
+
 76
+    sources = config.get('install_sources')
 77
+    keys = config.get('install_keys')
 78
+    if reactive.helpers.data_changed('apt.configure_sources', (sources, keys)):
 79
+        fetch.configure_sources(update=False,
 80
+                                sources_var='install_sources',
 81
+                                keys_var='install_keys')
 82
+        reactive.set_state('apt.needs_update')
 83
+
 84
+    extra_packages = sorted(config.get('extra_packages', '').split())
 85
+    if extra_packages:
 86
+        queue_install(extra_packages)
 87
+
 88
+
 89
+# Per https://github.com/juju-solutions/charms.reactive/issues/33,
 90
+# this module may be imported multiple times so ensure the
 91
+# initialization hook is only registered once. I have to piggy back
 92
+# onto the namespace of a module imported before reactive discovery
 93
+# to do this.
 94
+if not hasattr(reactive, '_apt_registered'):
 95
+    # We need to register this to run every hook, not just during install
 96
+    # and config-changed, to protect against race conditions. If we don't
 97
+    # do this, then the config in the hook environment may show updates
 98
+    # to running hooks well before the config-changed hook has been invoked
 99
+    # and the intialization provided an opertunity to be run.
100
+    hookenv.atstart(configure_sources)
101
+    reactive._apt_registered = True
Back to file index

deps/layer/layer-basic/.gitignore

1
--- 
2
+++ deps/layer/layer-basic/.gitignore
3
@@ -0,0 +1,5 @@
4
+*.pyc
5
+*~
6
+.ropeproject
7
+.settings
8
+.tox
Back to file index

deps/layer/layer-basic/Makefile

 1
--- 
 2
+++ deps/layer/layer-basic/Makefile
 3
@@ -0,0 +1,24 @@
 4
+#!/usr/bin/make
 5
+
 6
+all: lint unit_test
 7
+
 8
+
 9
+.PHONY: clean
10
+clean:
11
+	@rm -rf .tox
12
+
13
+.PHONY: apt_prereqs
14
+apt_prereqs:
15
+	@# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip)
16
+	@which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox)
17
+
18
+.PHONY: lint
19
+lint: apt_prereqs
20
+	@tox --notest
21
+	@PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests)
22
+	@charm proof
23
+
24
+.PHONY: unit_test
25
+unit_test: apt_prereqs
26
+	@echo Starting tests...
27
+	tox
Back to file index

deps/layer/layer-basic/README.md

  1
--- 
  2
+++ deps/layer/layer-basic/README.md
  3
@@ -0,0 +1,221 @@
  4
+# Overview
  5
+
  6
+This is the base layer for all charms [built using layers][building].  It
  7
+provides all of the standard Juju hooks and runs the
  8
+[charms.reactive.main][charms.reactive] loop for them.  It also bootstraps the
  9
+[charm-helpers][] and [charms.reactive][] libraries and all of their
 10
+dependencies for use by the charm.
 11
+
 12
+# Usage
 13
+
 14
+To create a charm layer using this base layer, you need only include it in
 15
+a `layer.yaml` file:
 16
+
 17
+```yaml
 18
+includes: ['layer:basic']
 19
+```
 20
+
 21
+This will fetch this layer from [interfaces.juju.solutions][] and incorporate
 22
+it into your charm layer.  You can then add handlers under the `reactive/`
 23
+directory.  Note that **any** file under `reactive/` will be expected to
 24
+contain handlers, whether as Python decorated functions or [executables][non-python]
 25
+using the [external handler protocol][].
 26
+
 27
+### Charm Dependencies
 28
+
 29
+Each layer can include a `wheelhouse.txt` file with Python requirement lines.
 30
+For example, this layer's `wheelhouse.txt` includes:
 31
+
 32
+```
 33
+pip>=7.0.0,<8.0.0
 34
+charmhelpers>=0.4.0,<1.0.0
 35
+charms.reactive>=0.1.0,<2.0.0
 36
+```
 37
+
 38
+All of these dependencies from each layer will be fetched (and updated) at build
 39
+time and will be automatically installed by this base layer before any reactive
 40
+handlers are run.
 41
+
 42
+Note that the `wheelhouse.txt` file is intended for **charm** dependencies only.
 43
+That is, for libraries that the charm code itself needs to do its job of deploying
 44
+and configuring the payload.  If the payload itself has Python dependencies, those
 45
+should be handled separately, by the charm.
 46
+
 47
+See [PyPI][pypi charms.X] for packages under the `charms.` namespace which might
 48
+be useful for your charm.
 49
+
 50
+### Layer Namespace
 51
+
 52
+Each layer has a reserved section in the `charms.layer.` Python package namespace,
 53
+which it can populate by including a `lib/charms/layer/<layer-name>.py` file or
 54
+by placing files under `lib/charms/layer/<layer-name>/`.  (If the layer name
 55
+includes hyphens, replace them with underscores.)  These can be helpers that the
 56
+layer uses internally, or it can expose classes or functions to be used by other
 57
+layers to interact with that layer.
 58
+
 59
+For example, a layer named `foo` could include a `lib/charms/layer/foo.py` file
 60
+with some helper functions that other layers could access using:
 61
+
 62
+```python
 63
+from charms.layer.foo import my_helper
 64
+```
 65
+
 66
+### Layer Options
 67
+
 68
+Any layer can define options in its `layer.yaml`.  Those options can then be set
 69
+by other layers to change the behavior of your layer.  The options are defined
 70
+using [jsonschema][], which is the same way that [action paramters][] are defined.
 71
+
 72
+For example, the `foo` layer could include the following option definitons:
 73
+
 74
+```yaml
 75
+includes: ['layer:basic']
 76
+defines:  # define some options for this layer (the layer "foo")
 77
+  enable-bar:  # define an "enable-bar" option for this layer
 78
+    description: If true, enable support for "bar".
 79
+    type: boolean
 80
+    default: false
 81
+```
 82
+
 83
+A layer using `foo` could then set it:
 84
+
 85
+```yaml
 86
+includes: ['layer:foo']
 87
+options:
 88
+  foo:  # setting options for the "foo" layer
 89
+    enable-bar: true  # set the "enable-bar" option to true
 90
+```
 91
+
 92
+The `foo` layer can then use the `charms.layer.options` helper to load the values
 93
+for the options that it defined.  For example:
 94
+
 95
+```python
 96
+from charms import layer
 97
+
 98
+@when('state')
 99
+def do_thing():
100
+  layer_opts = layer.options('foo')  # load all of the options for the "foo" layer
101
+  if layer_opts['enable-bar']:  # check the value of the "enable-bar" option
102
+      hookenv.log("Bar is enabled")
103
+```
104
+
105
+You can also access layer options in other handlers, such as Bash, using
106
+the command-line interface:
107
+
108
+```bash
109
+. charms.reactive.sh
110
+
111
+@when 'state'
112
+function do_thing() {
113
+    if layer_option foo enable-bar; then
114
+        juju-log "Bar is enabled"
115
+        juju-log "bar-value is: $(layer_option foo bar-value)"
116
+    fi
117
+}
118
+
119
+reactive_handler_main
120
+```
121
+
122
+Note that options of type `boolean` will set the exit code, while other types
123
+will be printed out.
124
+
125
+# Hooks
126
+
127
+This layer provides hooks that other layers can react to using the decorators
128
+of the [charms.reactive][] library:
129
+
130
+  * `config-changed`
131
+  * `install`
132
+  * `leader-elected`
133
+  * `leader-settings-changed`
134
+  * `start`
135
+  * `stop`
136
+  * `upgrade-charm`
137
+  * `update-status`
138
+
139
+Other hooks are not implemented at this time. A new layer can implement storage
140
+or relation hooks in their own layer by putting them in the `hooks` directory.
141
+
142
+**Note:** Because `update-status` is invoked every 5 minutes, you should take
143
+care to ensure that your reactive handlers only invoke expensive operations
144
+when absolutely necessary.  It is recommended that you use helpers like
145
+[`@only_once`][], [`@when_file_changed`][], and [`data_changed`][] to ensure
146
+that handlers run only when necessary.
147
+
148
+# Layer Configuration
149
+
150
+This layer supports the following options, which can be set in `layer.yaml`:
151
+
152
+  * **packages**  A list of system packages to be installed before the reactive
153
+    handlers are invoked.
154
+
155
+  * **use_venv**  If set to true, the charm dependencies from the various
156
+    layers' `wheelhouse.txt` files will be installed in a Python virtualenv
157
+    located at `$CHARM_DIR/../.venv`.  This keeps charm dependencies from
158
+    conflicting with payload dependencies, but you must take care to preserve
159
+    the environment and interpreter if using `execl` or `subprocess`.
160
+
161
+  * **include_system_packages**  If set to true and using a venv, include
162
+    the `--system-site-packages` options to make system Python libraries
163
+    visible within the venv.
164
+
165
+An example `layer.yaml` using these options might be:
166
+
167
+```yaml
168
+includes: ['layer:basic']
169
+options:
170
+  basic:
171
+    packages: ['git']
172
+    use_venv: true
173
+    include_system_packages: true
174
+```
175
+
176
+
177
+# Reactive States
178
+
179
+This layer will set the following states:
180
+
181
+  * **`config.changed`**  Any config option has changed from its previous value.
182
+    This state is cleared automatically at the end of each hook invocation.
183
+
184
+  * **`config.changed.<option>`** A specific config option has changed.
185
+    **`<option>`** will be replaced by the config option name from `config.yaml`.
186
+    This state is cleared automatically at the end of each hook invocation.
187
+
188
+  * **`config.set.<option>`** A specific config option has a True or non-empty
189
+    value set.  **`<option>`** will be replaced by the config option name from
190
+    `config.yaml`.  This state is cleared automatically at the end of each hook
191
+    invocation.
192
+
193
+  * **`config.default.<option>`** A specific config option is set to its
194
+    default value.  **`<option>`** will be replaced by the config option name
195
+    from `config.yaml`.  This state is cleared automatically at the end of
196
+    each hook invocation.
197
+
198
+An example using the config states would be:
199
+
200
+```python
201
+@when('config.changed.my-opt')
202
+def my_opt_changed():
203
+    update_config()
204
+    restart_service()
205
+```
206
+
207
+
208
+# Actions
209
+
210
+This layer currently does not define any actions.
211
+
212
+
213
+[building]: https://jujucharms.com/docs/devel/authors-charm-building
214
+[charm-helpers]: https://pythonhosted.org/charmhelpers/
215
+[charms.reactive]: https://pythonhosted.org/charms.reactive/
216
+[interfaces.juju.solutions]: http://interfaces.juju.solutions/
217
+[non-python]: https://pythonhosted.org/charms.reactive/#non-python-reactive-handlers
218
+[external handler protocol]: https://pythonhosted.org/charms.reactive/charms.reactive.bus.html#charms.reactive.bus.ExternalHandler
219
+[jsonschema]: http://json-schema.org/
220
+[action paramters]: https://jujucharms.com/docs/stable/authors-charm-actions
221
+[pypi charms.X]: https://pypi.python.org/pypi?%3Aaction=search&term=charms.&submit=search
222
+[`@only_once`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.only_once
223
+[`@when_file_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.when_file_changed
224
+[`data_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.helpers.html#charms.reactive.helpers.data_changed
Back to file index

deps/layer/layer-basic/bin/layer_option

 1
--- 
 2
+++ deps/layer/layer-basic/bin/layer_option
 3
@@ -0,0 +1,24 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+import sys
 7
+sys.path.append('lib')
 8
+
 9
+import argparse
10
+from charms.layer import options
11
+
12
+
13
+parser = argparse.ArgumentParser(description='Access layer options.')
14
+parser.add_argument('section',
15
+                    help='the section, or layer, the option is from')
16
+parser.add_argument('option',
17
+                    help='the option to access')
18
+
19
+args = parser.parse_args()
20
+value = options(args.section).get(args.option, '')
21
+if isinstance(value, bool):
22
+    sys.exit(0 if value else 1)
23
+elif isinstance(value, list):
24
+    for val in value:
25
+        print(val)
26
+else:
27
+    print(value)
Back to file index

deps/layer/layer-basic/copyright

 1
--- 
 2
+++ deps/layer/layer-basic/copyright
 3
@@ -0,0 +1,9 @@
 4
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
 5
+
 6
+Files: *
 7
+Copyright: 2015, Canonical Ltd.
 8
+License: GPL-3
 9
+
10
+License: GPL-3
11
+ On Debian GNU/Linux system you can find the complete text of the
12
+ GPL-3 license in '/usr/share/common-licenses/GPL-3'
Back to file index

deps/layer/layer-basic/hooks/config-changed

 1
--- 
 2
+++ deps/layer/layer-basic/hooks/config-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

deps/layer/layer-basic/hooks/hook.template

 1
--- 
 2
+++ deps/layer/layer-basic/hooks/hook.template
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

deps/layer/layer-basic/hooks/install

 1
--- 
 2
+++ deps/layer/layer-basic/hooks/install
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

deps/layer/layer-basic/hooks/leader-elected

 1
--- 
 2
+++ deps/layer/layer-basic/hooks/leader-elected
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

deps/layer/layer-basic/hooks/leader-settings-changed

 1
--- 
 2
+++ deps/layer/layer-basic/hooks/leader-settings-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

deps/layer/layer-basic/hooks/start

 1
--- 
 2
+++ deps/layer/layer-basic/hooks/start
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

deps/layer/layer-basic/hooks/stop

 1
--- 
 2
+++ deps/layer/layer-basic/hooks/stop
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

deps/layer/layer-basic/hooks/update-status

 1
--- 
 2
+++ deps/layer/layer-basic/hooks/update-status
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

deps/layer/layer-basic/hooks/upgrade-charm

 1
--- 
 2
+++ deps/layer/layer-basic/hooks/upgrade-charm
 3
@@ -0,0 +1,28 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import os
 8
+import sys
 9
+sys.path.append('lib')
10
+
11
+# This is an upgrade-charm context, make sure we install latest deps
12
+if not os.path.exists('wheelhouse/.upgrade'):
13
+    open('wheelhouse/.upgrade', 'w').close()
14
+    if os.path.exists('wheelhouse/.bootstrapped'):
15
+        os.unlink('wheelhouse/.bootstrapped')
16
+else:
17
+    os.unlink('wheelhouse/.upgrade')
18
+
19
+from charms.layer import basic
20
+basic.bootstrap_charm_deps()
21
+basic.init_config_states()
22
+
23
+
24
+# This will load and run the appropriate @hook and other decorated
25
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
26
+# and $CHARM_DIR/hooks/relations.
27
+#
28
+# See https://jujucharms.com/docs/stable/authors-charm-building
29
+# for more information on this pattern.
30
+from charms.reactive import main
31
+main()
Back to file index

deps/layer/layer-basic/layer.yaml

 1
--- 
 2
+++ deps/layer/layer-basic/layer.yaml
 3
@@ -0,0 +1,18 @@
 4
+defines:
 5
+  packages:
 6
+    type: array
 7
+    default: []
 8
+    description: Additional packages to be installed at time of bootstrap
 9
+  use_venv:
10
+    type: boolean
11
+    default: false
12
+    description: >
13
+      Install charm dependencies (wheelhouse) into a Python virtual environment
14
+      to help avoid conflicts with other charms or libraries on the machine.
15
+  include_system_packages:
16
+    type: boolean
17
+    default: false
18
+    description: >
19
+      If using a virtual environment, allow the venv to see Python packages
20
+      installed at the system level.  This reduces isolation, but is necessary
21
+      to use Python packages installed via apt-get.
Back to file index

deps/layer/layer-basic/lib/charms/layer/__init__.py

 1
--- 
 2
+++ deps/layer/layer-basic/lib/charms/layer/__init__.py
 3
@@ -0,0 +1,21 @@
 4
+import os
 5
+
 6
+
 7
+class LayerOptions(dict):
 8
+    def __init__(self, layer_file, section=None):
 9
+        import yaml  # defer, might not be available until bootstrap
10
+        with open(layer_file) as f:
11
+            layer = yaml.safe_load(f.read())
12
+        opts = layer.get('options', {})
13
+        if section and section in opts:
14
+            super(LayerOptions, self).__init__(opts.get(section))
15
+        else:
16
+            super(LayerOptions, self).__init__(opts)
17
+
18
+
19
+def options(section=None, layer_file=None):
20
+    if not layer_file:
21
+        base_dir = os.environ.get('CHARM_DIR', os.getcwd())
22
+        layer_file = os.path.join(base_dir, 'layer.yaml')
23
+
24
+    return LayerOptions(layer_file, section)
Back to file index

deps/layer/layer-basic/lib/charms/layer/basic.py

  1
--- 
  2
+++ deps/layer/layer-basic/lib/charms/layer/basic.py
  3
@@ -0,0 +1,159 @@
  4
+import os
  5
+import sys
  6
+import shutil
  7
+import platform
  8
+from glob import glob
  9
+from subprocess import check_call
 10
+
 11
+from charms.layer.execd import execd_preinstall
 12
+
 13
+
 14
+def bootstrap_charm_deps():
 15
+    """
 16
+    Set up the base charm dependencies so that the reactive system can run.
 17
+    """
 18
+    # execd must happen first, before any attempt to install packages or
 19
+    # access the network, because sites use this hook to do bespoke
 20
+    # configuration and install secrets so the rest of this bootstrap
 21
+    # and the charm itself can actually succeed. This call does nothing
 22
+    # unless the operator has created and populated $CHARM_DIR/exec.d.
 23
+    execd_preinstall()
 24
+    # ensure that $CHARM_DIR/bin is on the path, for helper scripts
 25
+    os.environ['PATH'] += ':%s' % os.path.join(os.environ['CHARM_DIR'], 'bin')
 26
+    venv = os.path.abspath('../.venv')
 27
+    vbin = os.path.join(venv, 'bin')
 28
+    vpip = os.path.join(vbin, 'pip')
 29
+    vpy = os.path.join(vbin, 'python')
 30
+    if os.path.exists('wheelhouse/.bootstrapped'):
 31
+        from charms import layer
 32
+        cfg = layer.options('basic')
 33
+        if cfg.get('use_venv') and '.venv' not in sys.executable:
 34
+            # activate the venv
 35
+            os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
 36
+            reload_interpreter(vpy)
 37
+        return
 38
+    # bootstrap wheelhouse
 39
+    if os.path.exists('wheelhouse'):
 40
+        with open('/root/.pydistutils.cfg', 'w') as fp:
 41
+            # make sure that easy_install also only uses the wheelhouse
 42
+            # (see https://github.com/pypa/pip/issues/410)
 43
+            charm_dir = os.environ['CHARM_DIR']
 44
+            fp.writelines([
 45
+                "[easy_install]\n",
 46
+                "allow_hosts = ''\n",
 47
+                "find_links = file://{}/wheelhouse/\n".format(charm_dir),
 48
+            ])
 49
+        apt_install(['python3-pip', 'python3-setuptools', 'python3-yaml'])
 50
+        from charms import layer
 51
+        cfg = layer.options('basic')
 52
+        # include packages defined in layer.yaml
 53
+        apt_install(cfg.get('packages', []))
 54
+        # if we're using a venv, set it up
 55
+        if cfg.get('use_venv'):
 56
+            if not os.path.exists(venv):
 57
+                distname, version, series = platform.linux_distribution()
 58
+                if series in ('precise', 'trusty'):
 59
+                    apt_install(['python-virtualenv'])
 60
+                else:
 61
+                    apt_install(['virtualenv'])
 62
+                cmd = ['virtualenv', '-ppython3', '--never-download', venv]
 63
+                if cfg.get('include_system_packages'):
 64
+                    cmd.append('--system-site-packages')
 65
+                check_call(cmd)
 66
+            os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
 67
+            pip = vpip
 68
+        else:
 69
+            pip = 'pip3'
 70
+            # save a copy of system pip to prevent `pip3 install -U pip`
 71
+            # from changing it
 72
+            if os.path.exists('/usr/bin/pip'):
 73
+                shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save')
 74
+        # need newer pip, to fix spurious Double Requirement error:
 75
+        # https://github.com/pypa/pip/issues/56
 76
+        check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse',
 77
+                    'pip'])
 78
+        # install the rest of the wheelhouse deps
 79
+        check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] +
 80
+                   glob('wheelhouse/*'))
 81
+        if not cfg.get('use_venv'):
 82
+            # restore system pip to prevent `pip3 install -U pip`
 83
+            # from changing it
 84
+            if os.path.exists('/usr/bin/pip.save'):
 85
+                shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip')
 86
+                os.remove('/usr/bin/pip.save')
 87
+        os.remove('/root/.pydistutils.cfg')
 88
+        # flag us as having already bootstrapped so we don't do it again
 89
+        open('wheelhouse/.bootstrapped', 'w').close()
 90
+        # Ensure that the newly bootstrapped libs are available.
 91
+        # Note: this only seems to be an issue with namespace packages.
 92
+        # Non-namespace-package libs (e.g., charmhelpers) are available
 93
+        # without having to reload the interpreter. :/
 94
+        reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
 95
+
 96
+
 97
+def reload_interpreter(python):
 98
+    """
 99
+    Reload the python interpreter to ensure that all deps are available.
100
+
101
+    Newly installed modules in namespace packages sometimes seemt to
102
+    not be picked up by Python 3.
103
+    """
104
+    os.execle(python, python, sys.argv[0], os.environ)
105
+
106
+
107
+def apt_install(packages):
108
+    """
109
+    Install apt packages.
110
+
111
+    This ensures a consistent set of options that are often missed but
112
+    should really be set.
113
+    """
114
+    if isinstance(packages, (str, bytes)):
115
+        packages = [packages]
116
+
117
+    env = os.environ.copy()
118
+
119
+    if 'DEBIAN_FRONTEND' not in env:
120
+        env['DEBIAN_FRONTEND'] = 'noninteractive'
121
+
122
+    cmd = ['apt-get',
123
+           '--option=Dpkg::Options::=--force-confold',
124
+           '--assume-yes',
125
+           'install']
126
+    check_call(cmd + packages, env=env)
127
+
128
+
129
+def init_config_states():
130
+    import yaml
131
+    from charmhelpers.core import hookenv
132
+    from charms.reactive import set_state
133
+    from charms.reactive import toggle_state
134
+    config = hookenv.config()
135
+    config_defaults = {}
136
+    config_defs = {}
137
+    config_yaml = os.path.join(hookenv.charm_dir(), 'config.yaml')
138
+    if os.path.exists(config_yaml):
139
+        with open(config_yaml) as fp:
140
+            config_defs = yaml.load(fp).get('options', {})
141
+            config_defaults = {key: value.get('default')
142
+                               for key, value in config_defs.items()}
143
+    for opt in config_defs.keys():
144
+        if config.changed(opt):
145
+            set_state('config.changed')
146
+            set_state('config.changed.{}'.format(opt))
147
+        toggle_state('config.set.{}'.format(opt), config.get(opt))
148
+        toggle_state('config.default.{}'.format(opt),
149
+                     config.get(opt) == config_defaults[opt])
150
+    hookenv.atexit(clear_config_states)
151
+
152
+
153
+def clear_config_states():
154
+    from charmhelpers.core import hookenv, unitdata
155
+    from charms.reactive import remove_state
156
+    config = hookenv.config()
157
+    remove_state('config.changed')
158
+    for opt in config.keys():
159
+        remove_state('config.changed.{}'.format(opt))
160
+        remove_state('config.set.{}'.format(opt))
161
+        remove_state('config.default.{}'.format(opt))
162
+    unitdata.kv().flush()
Back to file index

deps/layer/layer-basic/lib/charms/layer/execd.py

  1
--- 
  2
+++ deps/layer/layer-basic/lib/charms/layer/execd.py
  3
@@ -0,0 +1,138 @@
  4
+# Copyright 2014-2016 Canonical Limited.
  5
+#
  6
+# This file is part of layer-basic, the reactive base layer for Juju.
  7
+#
  8
+# charm-helpers is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU Lesser General Public License version 3 as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# charm-helpers is distributed in the hope that it will be useful,
 13
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
 14
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15
+# GNU Lesser General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU Lesser General Public License
 18
+# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+# This module may only import from the Python standard library.
 21
+import os
 22
+import sys
 23
+import subprocess
 24
+import time
 25
+
 26
+'''
 27
+execd/preinstall
 28
+
 29
+It is often necessary to configure and reconfigure machines
 30
+after provisioning, but before attempting to run the charm.
 31
+Common examples are specialized network configuration, enabling
 32
+of custom hardware, non-standard disk partitioning and filesystems,
 33
+adding secrets and keys required for using a secured network.
 34
+
 35
+The reactive framework's base layer invokes this mechanism as
 36
+early as possible, before any network access is made or dependencies
 37
+unpacked or non-standard modules imported (including the charms.reactive
 38
+framework itself).
 39
+
 40
+Operators needing to use this functionality may branch a charm and
 41
+create an exec.d directory in it. The exec.d directory in turn contains
 42
+one or more subdirectories, each of which contains an executable called
 43
+charm-pre-install and any other required resources. The charm-pre-install
 44
+executables are run, and if successful, state saved so they will not be
 45
+run again.
 46
+
 47
+    $CHARM_DIR/exec.d/mynamespace/charm-pre-install
 48
+
 49
+An alternative to branching a charm is to compose a new charm that contains
 50
+the exec.d directory, using the original charm as a layer,
 51
+
 52
+A charm author could also abuse this mechanism to modify the charm
 53
+environment in unusual ways, but for most purposes it is saner to use
 54
+charmhelpers.core.hookenv.atstart().
 55
+'''
 56
+
 57
+
 58
+def default_execd_dir():
 59
+    return os.path.join(os.environ['CHARM_DIR'], 'exec.d')
 60
+
 61
+
 62
+def execd_module_paths(execd_dir=None):
 63
+    """Generate a list of full paths to modules within execd_dir."""
 64
+    if not execd_dir:
 65
+        execd_dir = default_execd_dir()
 66
+
 67
+    if not os.path.exists(execd_dir):
 68
+        return
 69
+
 70
+    for subpath in os.listdir(execd_dir):
 71
+        module = os.path.join(execd_dir, subpath)
 72
+        if os.path.isdir(module):
 73
+            yield module
 74
+
 75
+
 76
+def execd_submodule_paths(command, execd_dir=None):
 77
+    """Generate a list of full paths to the specified command within exec_dir.
 78
+    """
 79
+    for module_path in execd_module_paths(execd_dir):
 80
+        path = os.path.join(module_path, command)
 81
+        if os.access(path, os.X_OK) and os.path.isfile(path):
 82
+            yield path
 83
+
 84
+
 85
+def execd_sentinel_path(submodule_path):
 86
+    module_path = os.path.dirname(submodule_path)
 87
+    execd_path = os.path.dirname(module_path)
 88
+    module_name = os.path.basename(module_path)
 89
+    submodule_name = os.path.basename(submodule_path)
 90
+    return os.path.join(execd_path,
 91
+                        '.{}_{}.done'.format(module_name, submodule_name))
 92
+
 93
+
 94
+def execd_run(command, execd_dir=None, stop_on_error=True, stderr=None):
 95
+    """Run command for each module within execd_dir which defines it."""
 96
+    if stderr is None:
 97
+        stderr = sys.stdout
 98
+    for submodule_path in execd_submodule_paths(command, execd_dir):
 99
+        # Only run each execd once. We cannot simply run them in the
100
+        # install hook, as potentially storage hooks are run before that.
101
+        # We cannot rely on them being idempotent.
102
+        sentinel = execd_sentinel_path(submodule_path)
103
+        if os.path.exists(sentinel):
104
+            continue
105
+
106
+        try:
107
+            subprocess.check_call([submodule_path], stderr=stderr,
108
+                                  universal_newlines=True)
109
+            with open(sentinel, 'w') as f:
110
+                f.write('{} ran successfully {}\n'.format(submodule_path,
111
+                                                          time.ctime()))
112
+                f.write('Removing this file will cause it to be run again\n')
113
+        except subprocess.CalledProcessError as e:
114
+            # Logs get the details. We can't use juju-log, as the
115
+            # output may be substantial and exceed command line
116
+            # length limits.
117
+            print("ERROR ({}) running {}".format(e.returncode, e.cmd),
118
+                  file=stderr)
119
+            print("STDOUT<<EOM", file=stderr)
120
+            print(e.output, file=stderr)
121
+            print("EOM", file=stderr)
122
+
123
+            # Unit workload status gets a shorter fail message.
124
+            short_path = os.path.relpath(submodule_path)
125
+            block_msg = "Error ({}) running {}".format(e.returncode,
126
+                                                       short_path)
127
+            try:
128
+                subprocess.check_call(['status-set', 'blocked', block_msg],
129
+                                      universal_newlines=True)
130
+                if stop_on_error:
131
+                    sys.exit(0)  # Leave unit in blocked state.
132
+            except Exception:
133
+                pass  # We care about the exec.d/* failure, not status-set.
134
+
135
+            if stop_on_error:
136
+                sys.exit(e.returncode or 1)  # Error state for pre-1.24 Juju
137
+
138
+
139
+def execd_preinstall(execd_dir=None):
140
+    """Run charm-pre-install for each module within execd_dir."""
141
+    execd_run('charm-pre-install', execd_dir=execd_dir)
Back to file index

deps/layer/layer-basic/metadata.yaml

1
--- 
2
+++ deps/layer/layer-basic/metadata.yaml
3
@@ -0,0 +1 @@
4
+{}
Back to file index

deps/layer/layer-basic/requirements.txt

1
--- 
2
+++ deps/layer/layer-basic/requirements.txt
3
@@ -0,0 +1,2 @@
4
+flake8
5
+pytest
Back to file index

deps/layer/layer-basic/tox.ini

 1
--- 
 2
+++ deps/layer/layer-basic/tox.ini
 3
@@ -0,0 +1,12 @@
 4
+[tox]
 5
+skipsdist=True
 6
+envlist = py34, py35
 7
+skip_missing_interpreters = True
 8
+
 9
+[testenv]
10
+commands = py.test -v
11
+deps =
12
+    -r{toxinidir}/requirements.txt
13
+
14
+[flake8]
15
+exclude=docs
Back to file index

deps/layer/layer-basic/wheelhouse.txt

1
--- 
2
+++ deps/layer/layer-basic/wheelhouse.txt
3
@@ -0,0 +1,3 @@
4
+pip>=7.0.0,<8.2.0
5
+charmhelpers>=0.4.0,<1.0.0
6
+charms.reactive>=0.1.0,<2.0.0
Back to file index

deps/layer/layer-leadership/.gitignore

1
--- 
2
+++ deps/layer/layer-leadership/.gitignore
3
@@ -0,0 +1,2 @@
4
+*~
5
+*.pyc
Back to file index

deps/layer/layer-leadership/README.md

 1
--- 
 2
+++ deps/layer/layer-leadership/README.md
 3
@@ -0,0 +1,88 @@
 4
+# Leadership Layer for Juju Charms
 5
+
 6
+The Leadership layer is for charm-tools and 'charm build', making it
 7
+easier for layered charms to deal with Juju leadership.
 8
+
 9
+This layer will initialize charms.reactive states, allowing you to
10
+write handlers that will be activated by these states. It allows you
11
+to completely avoid writing leader-elected and leader-settings-changed
12
+hooks. As a simple example, these two handlers are all that is required
13
+to make the leader unit generate a password if it is not already set,
14
+and have the shared password stored in a file on all units:
15
+
16
+```python
17
+import charms.leadership
18
+from charmhelpers.core.host import pwgen
19
+
20
+
21
+@when('leadership.is_leader')
22
+@when_not('leadership.set.admin_password')
23
+def generate_secret():
24
+    charms.leadership.leader_set(admin_password=pwgen())
25
+
26
+
27
+@when('leadership.changed.admin_password')
28
+def store_secret():
29
+    write_file('/etc/foopass', leader_get('admin_password'))
30
+```
31
+
32
+
33
+## States
34
+
35
+The following states are set appropriately on startup, before any @hook
36
+decorated methods are invoked:
37
+
38
+* `leadership.is_leader`
39
+
40
+  This state is set when the unit is the leader. The unit will remain
41
+  the leader for the remainder of the hook, but may not be leader in
42
+  future hooks.
43
+
44
+* `leadership.set.{varname}`
45
+
46
+  This state is set for each leadership setting (ie. the
47
+  `leadership.set.foo` state will be set if the leader has set
48
+  the foo leadership setting to any value). It will remain
49
+  set for the remainder of the hook, unless the unit is the leader
50
+  and calls `reactive.leadership.leader_set()` and resets the value
51
+  to None.
52
+
53
+* `leadership.changed.{varname}`
54
+
55
+  This state is set for each leadership setting that has changed
56
+  since the last hook. It will remain set for the remainder of the
57
+  hook. It will not be set in the next hook, unless the leader has
58
+  changed the leadership setting yet again.
59
+
60
+* `leadership.changed`
61
+
62
+  One or more leadership settings has changed since the last hook.
63
+  This state will remain set for the remainder of the hook. It will
64
+  not be set in the next hook, unless the leader has made further
65
+  changes.
66
+
67
+
68
+## Methods
69
+
70
+The `charms.leadership` module exposes the `leader_set()` and
71
+`leader_get()` methods, which match the methods found in the
72
+`charmhelpers.core.hookenv` module. `reactive.leadership.leader_set()`
73
+should be used instead of the charmhelpers function to ensure that
74
+the reactive state is updated when the leadership settings are. If you
75
+do not do this, then you risk handlers waiting on these states to not
76
+be run on the leader (because when the leader changes settings, it 
77
+triggers leader-settings-changed hooks on the follower units but
78
+no hooks on itself).
79
+
80
+
81
+## Support
82
+
83
+This layer is maintained on Launchpad by
84
+Stuart Bishop (stuart.bishop@canonical.com).
85
+
86
+Code is available using git at git+ssh://git.launchpad.net/layer-leadership.
87
+
88
+Bug reports can be made at https://bugs.launchpad.net/layer-leadership.
89
+
90
+Queries and comments can be made on the Juju mailing list, Juju IRC
91
+channels, or at https://answers.launchpad.net/layer-leadership.
Back to file index

deps/layer/layer-leadership/copyright

 1
--- 
 2
+++ deps/layer/layer-leadership/copyright
 3
@@ -0,0 +1,15 @@
 4
+Copyright 2015-2016 Canonical Ltd.
 5
+
 6
+This file is part of the Leadership Layer for Juju.
 7
+
 8
+This program is free software: you can redistribute it and/or modify
 9
+it under the terms of the GNU General Public License version 3, as
10
+published by the Free Software Foundation.
11
+
12
+This program is distributed in the hope that it will be useful, but
13
+WITHOUT ANY WARRANTY; without even the implied warranties of
14
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+PURPOSE.  See the GNU General Public License for more details.
16
+
17
+You should have received a copy of the GNU General Public License
18
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
Back to file index

deps/layer/layer-leadership/hooks/leader-elected

 1
--- 
 2
+++ deps/layer/layer-leadership/hooks/leader-elected
 3
@@ -0,0 +1,18 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer.basic import bootstrap_charm_deps
11
+bootstrap_charm_deps()
12
+
13
+
14
+# This will load and run the appropriate @hook and other decorated
15
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
16
+# and $CHARM_DIR/hooks/relations.
17
+#
18
+# See https://jujucharms.com/docs/stable/authors-charm-building
19
+# for more information on this pattern.
20
+from charms.reactive import main
21
+main()
Back to file index

deps/layer/layer-leadership/hooks/leader-settings-changed

 1
--- 
 2
+++ deps/layer/layer-leadership/hooks/leader-settings-changed
 3
@@ -0,0 +1,18 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer.basic import bootstrap_charm_deps
11
+bootstrap_charm_deps()
12
+
13
+
14
+# This will load and run the appropriate @hook and other decorated
15
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
16
+# and $CHARM_DIR/hooks/relations.
17
+#
18
+# See https://jujucharms.com/docs/stable/authors-charm-building
19
+# for more information on this pattern.
20
+from charms.reactive import main
21
+main()
Back to file index

deps/layer/layer-leadership/layer.yaml

 1
--- 
 2
+++ deps/layer/layer-leadership/layer.yaml
 3
@@ -0,0 +1,17 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+includes:
20
+    - layer:basic
Back to file index

deps/layer/layer-leadership/lib/charms/__init__.py

1
--- 
2
+++ deps/layer/layer-leadership/lib/charms/__init__.py
3
@@ -0,0 +1,2 @@
4
+from pkgutil import extend_path
5
+__path__ = extend_path(__path__, __name__)
Back to file index

deps/layer/layer-leadership/lib/charms/leadership.py

 1
--- 
 2
+++ deps/layer/layer-leadership/lib/charms/leadership.py
 3
@@ -0,0 +1,58 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+
20
+from charmhelpers.core import hookenv
21
+from charmhelpers.core import unitdata
22
+
23
+from charms import reactive
24
+from charms.reactive import not_unless
25
+
26
+
27
+__all__ = ['leader_get', 'leader_set']
28
+
29
+
30
+@not_unless('leadership.is_leader')
31
+def leader_set(settings=None, **kw):
32
+    '''Change leadership settings, per charmhelpers.core.hookenv.leader_set.
33
+
34
+    The leadership.set.{key} reactive state will be set while the
35
+    leadership hook environment setting remains set.
36
+
37
+    Changed leadership settings will set the leadership.changed.{key}
38
+    and leadership.changed states. These states will remain set until
39
+    the following hook.
40
+
41
+    These state changes take effect immediately on the leader, and
42
+    in future hooks run on non-leaders. In this way both leaders and
43
+    non-leaders can share handlers, waiting on these states.
44
+    '''
45
+    settings = settings or {}
46
+    settings.update(kw)
47
+    previous = unitdata.kv().getrange('leadership.settings.', strip=True)
48
+
49
+    for key, value in settings.items():
50
+        if value != previous.get(key):
51
+            reactive.set_state('leadership.changed.{}'.format(key))
52
+            reactive.set_state('leadership.changed')
53
+        reactive.helpers.toggle_state('leadership.set.{}'.format(key),
54
+                                      value is not None)
55
+    hookenv.leader_set(settings)
56
+    unitdata.kv().update(settings, prefix='leadership.settings.')
57
+
58
+
59
+def leader_get(attribute=None):
60
+    '''Return leadership settings, per charmhelpers.core.hookenv.leader_get.'''
61
+    return hookenv.leader_get(attribute)
Back to file index

deps/layer/layer-leadership/reactive/leadership.py

 1
--- 
 2
+++ deps/layer/layer-leadership/reactive/leadership.py
 3
@@ -0,0 +1,68 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+
20
+from charmhelpers.core import hookenv
21
+from charmhelpers.core import unitdata
22
+
23
+from charms import reactive
24
+from charms.leadership import leader_get, leader_set
25
+
26
+
27
+__all__ = ['leader_get', 'leader_set']  # Backwards compatibility
28
+
29
+
30
+def initialize_leadership_state():
31
+    '''Initialize leadership.* states from the hook environment.
32
+
33
+    Invoked by hookenv.atstart() so states are available in
34
+    @hook decorated handlers.
35
+    '''
36
+    is_leader = hookenv.is_leader()
37
+    if is_leader:
38
+        hookenv.log('Initializing Leadership Layer (is leader)')
39
+    else:
40
+        hookenv.log('Initializing Leadership Layer (is follower)')
41
+
42
+    reactive.helpers.toggle_state('leadership.is_leader', is_leader)
43
+
44
+    previous = unitdata.kv().getrange('leadership.settings.', strip=True)
45
+    current = hookenv.leader_get()
46
+
47
+    # Handle deletions.
48
+    for key in set(previous.keys()) - set(current.keys()):
49
+        current[key] = None
50
+
51
+    any_changed = False
52
+    for key, value in current.items():
53
+        reactive.helpers.toggle_state('leadership.changed.{}'.format(key),
54
+                                      value != previous.get(key))
55
+        if value != previous.get(key):
56
+            any_changed = True
57
+        reactive.helpers.toggle_state('leadership.set.{}'.format(key),
58
+                                      value is not None)
59
+    reactive.helpers.toggle_state('leadership.changed', any_changed)
60
+
61
+    unitdata.kv().update(current, prefix='leadership.settings.')
62
+
63
+
64
+# Per https://github.com/juju-solutions/charms.reactive/issues/33,
65
+# this module may be imported multiple times so ensure the
66
+# initialization hook is only registered once. I have to piggy back
67
+# onto the namespace of a module imported before reactive discovery
68
+# to do this.
69
+if not hasattr(reactive, '_leadership_registered'):
70
+    hookenv.atstart(initialize_leadership_state)
71
+    reactive._leadership_registered = True
Back to file index

files/archives/install-MFS-responsefile.xml

 1
--- 
 2
+++ files/archives/install-MFS-responsefile.xml
 3
@@ -0,0 +1,57 @@
 4
+<?xml version='1.0' encoding='UTF-8'?>
 5
+<agent-input>
 6
+  <variables>
 7
+    <variable name='sharedLocation' value='/root/IBM/IBMIMShared'/>
 8
+  </variables>
 9
+  <server>
10
+    <repository location='/root/Installfiles/MobileFirst_Platform_Server/disk1'/>
11
+  </server>
12
+  <profile id='IBM MobileFirst Platform Server' installLocation='/opt/ibm/MobileFirst_Platform_Server_liberty'>
13
+    <data key='eclipseLocation' value='/opt/ibm/MobileFirst_Platform_Server_liberty'/>
14
+    <data key='user.import.profile' value='false'/>
15
+    <data key='cic.selector.os' value='linux'/>
16
+    <data key='user.windows.profilesubdir' value='ibm\MobileFirst Platform Server'/>
17
+    <data key='user.shortcuts.folder3' value='IBM MobileFirst Platform Server 7.0_1'/>
18
+    <data key='user.update.minimal' value='false'/>
19
+    <data key='user.update.from.version' value=''/>
20
+    <data key='user.writable.data.user' value='root'/>
21
+    <data key='user.database.db2.port' value='50000'/>
22
+    <data key='user.database.db2.appcenter.dbname' value='APPCNTRA'/> 
23
+    <data key='user.database.db2.appcenter.schema' value='db2usr1'/>
24
+    <data key='user.database.db2.appcenter.password' value='db2databasepassword'/>
25
+    <data key='user.database.db2.password2' value='db2databasepassword'/>
26
+    <data key='user.database.db2.appcenter.username' value='db2inst1'/>
27
+    <data key='user.database.selection2' value='db2'/>
28
+    <data key='user.database.preinstalled' value='true'/>
29
+    <data key='user.database.db2.host' value='localhost'/>
30
+    <data key='user.database.db2.driver' value='/opt/ibm/db2/V10.5/java/db2jcc.jar'/>
31
+    <data key='user.database.db2.username' value='db2inst1'/>
32
+    <data key='user.appserver.selection2' value='was'/>
33
+    <data key='user.appserver.was85liberty.serverInstance_' value='waserver1'/>
34
+    <data key='user.appserver.was.profile' value='Liberty'/>
35
+    <data key='user.appserver.was.installdir' value='/root/IBM/WebSphere/wlp'/>
36
+    <data key='user.writable.data.group2' value=''/>
37
+    <data key='cic.selector.nl' value='en'/>
38
+  </profile>
39
+  <install modify='false'>
40
+    <!-- IBM® MobileFirst Platform Server 7.0.0 -->
41
+    <offering profile='IBM MobileFirst Platform Server' id='com.ibm.mobilefirst.foundation.server' version='7.0.0.00-20150312-0731' features='main.feature' installFixes='none'/>
42
+  </install>
43
+  <preference name='com.ibm.cic.common.core.preferences.eclipseCache' value='${sharedLocation}'/>
44
+  <preference name='com.ibm.cic.common.core.preferences.connectTimeout' value='30'/>
45
+  <preference name='com.ibm.cic.common.core.preferences.readTimeout' value='45'/>
46
+  <preference name='com.ibm.cic.common.core.preferences.downloadAutoRetryCount' value='0'/>
47
+  <preference name='offering.service.repositories.areUsed' value='true'/>
48
+  <preference name='com.ibm.cic.common.core.preferences.ssl.nonsecureMode' value='false'/>
49
+  <preference name='com.ibm.cic.common.core.preferences.http.disablePreemptiveAuthentication' value='false'/>
50
+  <preference name='http.ntlm.auth.kind' value='NTLM'/>
51
+  <preference name='http.ntlm.auth.enableIntegrated.win32' value='true'/>
52
+  <preference name='com.ibm.cic.common.core.preferences.preserveDownloadedArtifacts' value='true'/>
53
+  <preference name='com.ibm.cic.common.core.preferences.keepFetchedFiles' value='false'/>
54
+  <preference name='PassportAdvantageIsEnabled' value='false'/>
55
+  <preference name='com.ibm.cic.common.core.preferences.searchForUpdates' value='false'/>
56
+  <preference name='com.ibm.cic.agent.ui.displayInternalVersion' value='false'/>
57
+  <preference name='com.ibm.cic.common.sharedUI.showErrorLog' value='true'/>
58
+  <preference name='com.ibm.cic.common.sharedUI.showWarningLog' value='true'/>
59
+  <preference name='com.ibm.cic.common.sharedUI.showNoteLog' value='true'/>
60
+</agent-input>
Back to file index

files/archives/uninstall-worklight-responsefile.xml

 1
--- 
 2
+++ files/archives/uninstall-worklight-responsefile.xml
 3
@@ -0,0 +1,49 @@
 4
+<?xml version="1.0" encoding="UTF-8"?>
 5
+<!--
 6
+
 7
+    Licensed Materials - Property of IBM
 8
+    5725-I43 (C) Copyright IBM Corp. 2011, 2014. All Rights Reserved.
 9
+    US Government Users Restricted Rights - Use, duplication or
10
+    disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
11
+
12
+-->
13
+
14
+<!-- IBM MobileFirst Platform Server: sample uninstallation response file
15
+     for an installation that was initially a Worklight Server version 6.x. -->
16
+
17
+<agent-input>
18
+
19
+  <server>
20
+    <!-- The repositories where Installation Manager can find offerings.
21
+         URLs and absolute file names are accepted; they should point to
22
+         directories that contain a repository.config or diskTag.inf file.
23
+         To install a major release, you need one repository.
24
+         To install a fix pack release, you usually need two repositories.
25
+         To install an interim fix, you usually need two or three repositories.
26
+         For more details, see
27
+         http://pic.dhe.ibm.com/infocenter/install/v1r6/index.jsp?topic=%2Fcom.ibm.silentinstall12.doc%2Ftopics%2Fr_repository_types.html
28
+         http://ibm.biz/knowctr#SSHS8R_7.0.0/com.ibm.worklight.upgrade.doc/devenv/c_upgrade_to_srvr_prep_become_familiar_with_IBM_IM.html
29
+         http://ibm.biz/knowctr#SSHS8R_7.0.0/com.ibm.worklight.installconfig.doc/admin/r_silent_installation_parameters.html
30
+    -->
31
+    <!-- <repository location='http://packages.example.com/ibm/mfpserver-7.0/'/> -->
32
+       <!--  <repository location='/root/Installfiles/MobileFirst_Platform_Server/disk1/'/> -->
33
+           <repository location='/root/Installfiles/MobileFirst_Platform_Server/disk1/'/> 
34
+  </server>
35
+
36
+  <!-- The declaration of the Installation Manager package group, a.k.a. profile.
37
+       Make sure that the installLocation, if it exists, is empty. -->
38
+  <!--<profile id='IBM Worklight' installLocation='/opt/IBM/Worklight'> -->
39
+      <profile id='IBM MobileFirst Platform Server' installLocation='/opt/ibm/MobileFirst_Platform_Server_liberty'>
40
+
41
+    <!-- The settings 'eclipseLocation', 'user.import.profile',
42
+         'cic.selector.os', 'cic.selector.ws', 'cic.selector.arch',
43
+         'cic.selector.nl' don't need to be specified. -->
44
+
45
+  </profile>
46
+
47
+  <!-- Specify what Installation Manager should uninstall. -->
48
+  <uninstall modify='false'>
49
+    <offering id='com.ibm.mobilefirst.foundation.server' profile='IBM MobileFirst Platform Server'/>
50
+  </uninstall>
51
+
52
+</agent-input>
Back to file index

hooks/config-changed

 1
--- 
 2
+++ hooks/config-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/hook.template

 1
--- 
 2
+++ hooks/hook.template
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/install

 1
--- 
 2
+++ hooks/install
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/leader-elected

 1
--- 
 2
+++ hooks/leader-elected
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/leader-settings-changed

 1
--- 
 2
+++ hooks/leader-settings-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/mobilefirstserverdb-relation-broken

 1
--- 
 2
+++ hooks/mobilefirstserverdb-relation-broken
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/mobilefirstserverdb-relation-changed

 1
--- 
 2
+++ hooks/mobilefirstserverdb-relation-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/mobilefirstserverdb-relation-departed

 1
--- 
 2
+++ hooks/mobilefirstserverdb-relation-departed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/mobilefirstserverdb-relation-joined

 1
--- 
 2
+++ hooks/mobilefirstserverdb-relation-joined
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/relations/db2/README.md

 1
--- 
 2
+++ hooks/relations/db2/README.md
 3
@@ -0,0 +1,66 @@
 4
+# Overview
 5
+
 6
+This interface layer handles the communication between IBM DB2 and Consumer
 7
+charms. The provider end of this interface provides the IBM DB2 service. The
 8
+consumer part requires the existence of a provider to function.
 9
+
10
+## Usage
11
+
12
+### Provides
13
+
14
+This interface layer will set the following states, as appropriate:
15
+
16
+- `{relation_name}.connected`: The relation is established, but IBM DB2 may not
17
+yet have provided any connection or service information.
18
+
19
+- `{relation_name}.sshconfigured`: A consumer ssh key has been added to the
20
+IBM DB2 `authorized_keys` file.
21
+
22
+- `{relation_name}.ready`: IBM DB2 has established relation with the consumer
23
+charm and the SSH key has been exchanged. DB2 has provided its connection
24
+string information, and is ready to accept requests from the consumer.
25
+
26
+- `{relation_name}.departed`: The relation has been removed. Any cleanup
27
+related to the consumer charm should happen now on the IBM DB2 charm since the
28
+consumer is going away.
29
+
30
+
31
+### Requires
32
+
33
+Consumers like WAS, Mobile First Server, etc require this interface to connect
34
+to IBM DB2. This interface layer will set the following states, as appropriate:
35
+
36
+- `{relation_name}.connected`: The consumer charm has been related to IBM DB2.
37
+At this point, the consumer charm sends the SSH key to IBM DB2 charm.
38
+
39
+    For example, the consumer charm can send the ssh key to IBM DB2 charm as follows:
40
+
41
+    ```
42
+    @when 'db.connected'
43
+    @when_not 'db.sshconfigured'
44
+    function configure_sshkeys_dbs() {
45
+        SSH_PATH=/root/.ssh
46
+        if [ ! -f  $SSH_PATH/id_rsa.pub ]; then
47
+                juju-log "Setting up SSH keys."
48
+                ssh-keygen -t rsa -f $SSH_PATH/id_rsa -N ''
49
+        fi
50
+        key="`cat $SSH_PATH/id_rsa.pub`"
51
+        relation_call --state=db.connected set_ssh_keys "$key" || true
52
+    }
53
+    ```
54
+
55
+-  `{relation_name}.ready`: IBM DB2 is ready to send connection information.
56
+The consumer charm can access the IBM DB2 details from the interface using the
57
+following methods:
58
+
59
+   - get_db2_hostname() - host name of IBM DB2
60
+   - get_db2_port() - port number of IBM DB2
61
+   - get_db2_path() - the install location of IBM DB2
62
+   - get_dbusername() - User name created for the consumer service to connect to DB2
63
+   - get_dbuserpw() - Password for connecting to DB2
64
+   - get_db2_instance_name() - Instance name created for the consumer service
65
+
66
+
67
+- `{relation_name}.departed`: The relation has been removed. Any cleanup
68
+related to IBM DB2 should happen now on the consumer charm since IBM DB2 is
69
+going away.
Back to file index

hooks/relations/db2/interface.yaml

1
--- 
2
+++ hooks/relations/db2/interface.yaml
3
@@ -0,0 +1,4 @@
4
+name: db2
5
+summary: Facilitates communication between IBM DB2 and database consumers
6
+version: 1
7
+maintainer: "IBM Juju Support Team <jujusupp@us.ibm.com>"
Back to file index

hooks/relations/db2/provides.py

 1
--- 
 2
+++ hooks/relations/db2/provides.py
 3
@@ -0,0 +1,88 @@
 4
+from charms.reactive import hook
 5
+from charms.reactive import RelationBase
 6
+from charms.reactive import scopes
 7
+
 8
+
 9
+class db2Provides(RelationBase):
10
+    # Every unit connecting will get the same information
11
+    scope = scopes.SERVICE
12
+    # convenient way to provide accessor methods
13
+    auto_accessors = ['db2_path', 'db2_port']
14
+
15
+    @hook('{provides:db2}-relation-joined')
16
+    def joined(self):
17
+        conversation = self.conversation()
18
+        conversation.remove_state('{relation_name}.departed')
19
+        conversation.set_state('{relation_name}.connected')
20
+
21
+    @hook('{provides:db2}-relation-changed')
22
+    def changed(self):
23
+        conversation = self.conversation()
24
+        conversation.remove_state('{relation_name}.departed')
25
+        conversation.set_state('{relation_name}.connected')
26
+        if str(conversation.get_remote('ssh_key')) != "None":
27
+            conversation.set_state('{relation_name}.ready')
28
+
29
+    @hook('{provides:db2}-relation-departed')
30
+    def departed(self):
31
+        conversation = self.conversation()
32
+        conversation.remove_state('{relation_name}.connected')
33
+        conversation.remove_state('{relation_name}.ready')
34
+        conversation.set_state('{relation_name}.departed')
35
+
36
+    def dismiss(self, service):
37
+        conversation = self.conversation(service)
38
+        conversation.remove_state('{relation_name}.departed')
39
+
40
+    def reset_states(self, service):
41
+        conversation = self.conversation(service)
42
+        conversation.remove_state('{relation_name}.connected')
43
+        conversation.remove_state('{relation_name}.departed')
44
+        conversation.remove_state('{relation_name}.ready')
45
+
46
+    def set_db_details(self, service, db2_path, db2_port, hostname,
47
+                       dbusername, dbuserpw, db2_instance_name, dbs_created):
48
+        conversation = self.conversation(service)
49
+        conversation.set_remote(data={
50
+            'db2_ready': True,
51
+            'db2_path': db2_path,
52
+            'db2_port': db2_port,
53
+            'hostname': hostname,
54
+            'dbusername': dbusername,
55
+            'dbuserpw': dbuserpw,
56
+            'db2_instance_name': db2_instance_name,
57
+            'dbs_created': dbs_created,
58
+        })
59
+
60
+    def set_db_password(self, service, dbuserpw):
61
+        conversation = self.conversation(service)
62
+        conversation.set_local('dbuserpw', dbuserpw)
63
+
64
+    def get_db_password(self, service):
65
+        conversation = self.conversation(service)
66
+        return conversation.get_local('dbuserpw')
67
+
68
+    def set_sshconfigured(self, service):
69
+        conversation = self.conversation(service)
70
+        conversation.set_state('{relation_name}.sshconfigured')
71
+
72
+    def dismiss_sshconfigured(self, service):
73
+        conversation = self.conversation(service)
74
+        conversation.remove_state('{relation_name}.sshconfigured')
75
+
76
+    def get_sshkeys(self, service):
77
+        conversation = self.conversation(service)
78
+        return conversation.get_remote('ssh_key')
79
+
80
+    def get_dbnames(self, service):
81
+        conversation = self.conversation(service)
82
+        return conversation.get_remote('db_names')
83
+
84
+    def services(self):
85
+        """
86
+        Return a list of services requesting databases.
87
+        """
88
+        service = []
89
+        for conversation in self.conversations():
90
+            service.append(conversation.scope)
91
+        return service
Back to file index

hooks/relations/db2/requires.py

 1
--- 
 2
+++ hooks/relations/db2/requires.py
 3
@@ -0,0 +1,65 @@
 4
+# Licensed under the Apache License, Version 2.0 (the "License");
 5
+# you may not use this file except in compliance with the License.
 6
+# You may obtain a copy of the License at
 7
+#
 8
+#     http://www.apache.org/licenses/LICENSE-2.0
 9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+from charms.reactive import hook
17
+from charms.reactive import RelationBase
18
+from charms.reactive import scopes
19
+
20
+
21
+class db2Requires(RelationBase):
22
+    scope = scopes.GLOBAL
23
+    auto_accessors = ['ssh_key']
24
+
25
+    @hook('{requires:db2}-relation-joined')
26
+    def joined(self):
27
+        self.remove_state('{relation_name}.departed')
28
+        self.set_state('{relation_name}.connected')
29
+
30
+    @hook('{requires:db2}-relation-changed')
31
+    def changed(self):
32
+        self.remove_state('{relation_name}.departed')
33
+        self.set_state('{relation_name}.connected')
34
+        if str(self.get_remote('db2_port')) != "None":
35
+            self.set_state('{relation_name}.ready')
36
+
37
+    @hook('{requires:db2}-relation-departed')
38
+    def departed(self):
39
+        self.remove_state('{relation_name}.connected')
40
+        self.remove_state('{relation_name}.ready')
41
+        self.set_state('{relation_name}.departed')
42
+
43
+    def get_db2_port(self):
44
+        return self.get_remote('db2_port')
45
+
46
+    def get_db2_path(self):
47
+        return self.get_remote('db2_path')
48
+
49
+    def get_db2_hostname(self):
50
+        return self.get_remote('hostname')
51
+
52
+    def get_dbusername(self):
53
+        return self.get_remote('dbusername')
54
+
55
+    def get_dbuserpw(self):
56
+        return self.get_remote('dbuserpw')
57
+
58
+    def get_db2_instance_name(self):
59
+        return self.get_remote('db2_instance_name')
60
+
61
+    def get_db2_dbnames(self):
62
+        return self.get_remote('dbs_created')
63
+
64
+    def set_ssh_keys(self, ssh_key):
65
+        self.set_remote('ssh_key', ssh_key)
66
+
67
+    def set_dbs(self, db_names):
68
+        self.set_remote('db_names', db_names)
Back to file index

hooks/relations/http/README.md

 1
--- 
 2
+++ hooks/relations/http/README.md
 3
@@ -0,0 +1,68 @@
 4
+# Overview
 5
+
 6
+This interface layer implements the basic form of the `http` interface protocol,
 7
+which is used for things such as reverse-proxies, load-balanced servers, REST
 8
+service discovery, et cetera.
 9
+
10
+# Usage
11
+
12
+## Provides
13
+
14
+By providing the `http` interface, your charm is providing an HTTP server that
15
+can be load-balanced, reverse-proxied, used as a REST endpoint, etc.
16
+
17
+Your charm need only provide the port on which it is serving its content, as
18
+soon as the `{relation_name}.available` state is set:
19
+
20
+```python
21
+@when('website.available')
22
+def configure_website(website):
23
+    website.configure(port=hookenv.config('port'))
24
+```
25
+
26
+## Requires
27
+
28
+By requiring the `http` interface, your charm is consuming one or more HTTP
29
+servers, as a REST endpoint, to load-balance a set of servers, etc.
30
+
31
+Your charm should respond to the `{relation_name}.available` state, which
32
+indicates that there is at least one HTTP server connected.
33
+
34
+The `services()` method returns a list of available HTTP services and their
35
+associated hosts and ports.
36
+
37
+The return value is a list of dicts of the following form:
38
+
39
+```python
40
+[
41
+    {
42
+        'service_name': name_of_service,
43
+        'hosts': [
44
+            {
45
+                'hostname': address_of_host,
46
+                'port': port_for_host,
47
+            },
48
+            # ...
49
+        ],
50
+    },
51
+    # ...
52
+]
53
+```
54
+
55
+A trivial example of handling this interface would be:
56
+
57
+```python
58
+from charms.reactive.helpers import data_changed
59
+
60
+@when('reverseproxy.available')
61
+def update_reverse_proxy_config(reverseproxy):
62
+    services = reverseproxy.services()
63
+    if not data_changed('reverseproxy.services', services):
64
+        return
65
+    for service in services:
66
+        for host in service['hosts']:
67
+            hookenv.log('{} has a unit {}:{}'.format(
68
+                services['service_name'],
69
+                host['hostname'],
70
+                host['port']))
71
+```
Back to file index

hooks/relations/http/interface.yaml

1
--- 
2
+++ hooks/relations/http/interface.yaml
3
@@ -0,0 +1,4 @@
4
+name: http
5
+summary: Basic HTTP interface
6
+version: 1
7
+repo: https://git.launchpad.net/~bcsaller/charms/+source/http
Back to file index

hooks/relations/http/provides.py

 1
--- 
 2
+++ hooks/relations/http/provides.py
 3
@@ -0,0 +1,28 @@
 4
+from charmhelpers.core import hookenv
 5
+from charms.reactive import hook
 6
+from charms.reactive import RelationBase
 7
+from charms.reactive import scopes
 8
+
 9
+
10
+class HttpProvides(RelationBase):
11
+    scope = scopes.GLOBAL
12
+
13
+    @hook('{provides:http}-relation-{joined,changed}')
14
+    def changed(self):
15
+        self.set_state('{relation_name}.available')
16
+
17
+    @hook('{provides:http}-relation-{broken,departed}')
18
+    def broken(self):
19
+        self.remove_state('{relation_name}.available')
20
+
21
+    def configure(self, port, private_address=None, hostname=None):
22
+        if not hostname:
23
+            hostname = hookenv.unit_get('private-address')
24
+        if not private_address:
25
+            private_address = hookenv.unit_get('private-address')
26
+        relation_info = {
27
+            'hostname': hostname,
28
+            'private-address': private_address,
29
+            'port': port,
30
+        }
31
+        self.set_remote(**relation_info)
Back to file index

hooks/relations/http/requires.py

 1
--- 
 2
+++ hooks/relations/http/requires.py
 3
@@ -0,0 +1,58 @@
 4
+from charms.reactive import hook
 5
+from charms.reactive import RelationBase
 6
+from charms.reactive import scopes
 7
+
 8
+
 9
+class HttpRequires(RelationBase):
10
+    scope = scopes.UNIT
11
+
12
+    @hook('{requires:http}-relation-{joined,changed}')
13
+    def changed(self):
14
+        conv = self.conversation()
15
+        if conv.get_remote('port'):
16
+            # this unit's conversation has a port, so
17
+            # it is part of the set of available units
18
+            conv.set_state('{relation_name}.available')
19
+
20
+    @hook('{requires:http}-relation-{departed,broken}')
21
+    def broken(self):
22
+        conv = self.conversation()
23
+        conv.remove_state('{relation_name}.available')
24
+
25
+    def services(self):
26
+        """
27
+        Returns a list of available HTTP services and their associated hosts
28
+        and ports.
29
+
30
+        The return value is a list of dicts of the following form::
31
+
32
+            [
33
+                {
34
+                    'service_name': name_of_service,
35
+                    'hosts': [
36
+                        {
37
+                            'hostname': address_of_host,
38
+                            'port': port_for_host,
39
+                        },
40
+                        # ...
41
+                    ],
42
+                },
43
+                # ...
44
+            ]
45
+        """
46
+        services = {}
47
+        for conv in self.conversations():
48
+            service_name = conv.scope.split('/')[0]
49
+            service = services.setdefault(service_name, {
50
+                'service_name': service_name,
51
+                'hosts': [],
52
+            })
53
+            host = conv.get_remote('hostname') or \
54
+                conv.get_remote('private-address')
55
+            port = conv.get_remote('port')
56
+            if host and port:
57
+                service['hosts'].append({
58
+                    'hostname': host,
59
+                    'port': port,
60
+                })
61
+        return [s for s in services.values() if s['hosts']]
Back to file index

hooks/start

 1
--- 
 2
+++ hooks/start
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/stop

 1
--- 
 2
+++ hooks/stop
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/update-status

 1
--- 
 2
+++ hooks/update-status
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/upgrade-charm

 1
--- 
 2
+++ hooks/upgrade-charm
 3
@@ -0,0 +1,28 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import os
 8
+import sys
 9
+sys.path.append('lib')
10
+
11
+# This is an upgrade-charm context, make sure we install latest deps
12
+if not os.path.exists('wheelhouse/.upgrade'):
13
+    open('wheelhouse/.upgrade', 'w').close()
14
+    if os.path.exists('wheelhouse/.bootstrapped'):
15
+        os.unlink('wheelhouse/.bootstrapped')
16
+else:
17
+    os.unlink('wheelhouse/.upgrade')
18
+
19
+from charms.layer import basic
20
+basic.bootstrap_charm_deps()
21
+basic.init_config_states()
22
+
23
+
24
+# This will load and run the appropriate @hook and other decorated
25
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
26
+# and $CHARM_DIR/hooks/relations.
27
+#
28
+# See https://jujucharms.com/docs/stable/authors-charm-building
29
+# for more information on this pattern.
30
+from charms.reactive import main
31
+main()
Back to file index

hooks/website-relation-broken

 1
--- 
 2
+++ hooks/website-relation-broken
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/website-relation-changed

 1
--- 
 2
+++ hooks/website-relation-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/website-relation-departed

 1
--- 
 2
+++ hooks/website-relation-departed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/website-relation-joined

 1
--- 
 2
+++ hooks/website-relation-joined
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

icon.svg

 1
--- 
 2
+++ icon.svg
 3
@@ -0,0 +1,29 @@
 4
+<?xml version="1.0" encoding="UTF-8"?>
 5
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
 6
+<!-- Creator: CorelDRAW X6 -->
 7
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1in" height="0.999996in" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"
 8
+viewBox="0 0 1000 1000"
 9
+ xmlns:xlink="http://www.w3.org/1999/xlink">
10
+ <defs>
11
+    <linearGradient id="id0" gradientUnits="userSpaceOnUse" x1="500.002" y1="999.996" x2="500.002" y2="0">
12
+     <stop offset="0" stop-color="#1570B7"/>
13
+     <stop offset="1" stop-color="#71AFDE"/>
14
+    </linearGradient>
15
+    <mask id="id1">
16
+      <linearGradient id="id2" gradientUnits="userSpaceOnUse" x1="500.002" y1="58.4805" x2="500.002" y2="307.017">
17
+       <stop offset="0" stop-opacity="1" stop-color="white"/>
18
+       <stop offset="0.141176" stop-opacity="-50.702" stop-color="white"/>
19
+       <stop offset="1" stop-opacity="0" stop-color="white"/>
20
+      </linearGradient>
21
+     <rect fill="url(#id2)" width="1000" height="365"/>
22
+    </mask>
23
+ </defs>
24
+ <g id="Layer_x0020_1">
25
+  <metadata id="CorelCorpID_0Corel-Layer"/>
26
+  <g id="_182637696">
27
+   <path id="Background" fill="url(#id0)" d="M0 676l0 -352c0,-283 41,-324 324,-324l352 0c284,0 324,41 324,324l0 352c0,283 -40,324 -324,324l-352 0c-283,0 -324,-41 -324,-324z"/>
28
+   <path fill="#999999" mask="url(#id1)" d="M0 365l0 -41c0,-283 41,-324 324,-324l352 0c284,0 324,41 324,324l0 41c0,-283 -40,-324 -324,-324l-352 0c-283,0 -324,41 -324,324z"/>
29
+   <path fill="white" d="M406 572c1,0 2,16 2,20l-9 40c-9,23 -13,42 9,49l77 20c19,-1 21,-34 26,-48 13,-38 -1,-8 19,-43 2,-3 2,-5 4,-8 2,-2 3,-4 5,-6 2,-4 3,-4 5,-7 2,-3 3,-6 4,-8l16 -21c4,-5 8,-7 13,-13 3,-3 9,-7 12,-12 9,-15 20,-29 24,-51 3,-20 6,-53 -6,-74 -4,-8 -1,-9 -13,-24 -7,-8 -2,-6 -18,-19l-21 -15c-10,-2 -12,-4 -20,-7 -8,-2 -41,-2 -49,0 -5,1 -29,10 -35,14 -4,3 -4,4 -8,6 -5,3 -4,2 -7,6 -10,9 -16,15 -24,27 -16,25 -21,52 -18,82 2,16 7,26 8,43l4 49zm222 -191c2,2 50,18 61,29 29,30 52,18 52,-8 0,-24 -31,-22 -50,-24 -11,-1 -58,-9 -63,3zm-85 -85c6,-2 15,-22 20,-28 3,-3 3,-2 5,-6 14,-23 41,-39 44,-43 19,-24 -13,-54 -37,-36 -4,3 -9,10 -10,15l-10 27c-9,22 -3,3 -7,30 -2,11 -10,28 -5,41zm72 31c5,0 12,-2 17,-3 15,-4 39,5 64,5 15,0 30,-9 38,-20 4,-5 4,-7 4,-15 1,-14 0,-19 -7,-28 -20,-23 -57,-11 -78,5 -4,3 -6,6 -12,10 -16,13 -14,15 -21,22 -3,3 -23,24 -5,24zm-214 379c-11,3 -8,19 -9,29l-6 105c0,29 3,37 25,49 18,11 43,-2 62,-4 27,-4 18,-29 19,-52l1 -71c-2,-32 1,-37 -32,-42 -13,-2 -47,-18 -60,-14zm-26 -132c-15,-2 -25,4 -39,-3 -26,-14 -26,-75 -26,-99 0,-13 6,-63 9,-75 2,-6 4,-10 5,-16 1,-6 0,-11 2,-18l15 -48c2,-6 4,-7 6,-15 2,-6 9,-24 12,-29 10,-18 5,-11 11,-23l19 -31c22,-30 35,-43 67,-58 24,-12 40,9 46,-33 1,-12 -2,-17 -15,-18 -13,-3 -38,-2 -50,1 -10,3 -30,11 -39,17 -2,1 -5,2 -8,4l-30 25 -19 23c-2,3 -3,4 -5,7l-22 33c-7,16 -12,22 -17,38 -1,3 -8,16 -12,29 -1,3 -1,7 -2,11 -2,4 -3,6 -5,9 -2,7 -13,58 -14,67 -4,33 -6,67 -3,100 1,16 7,57 12,70 2,6 7,25 15,39 3,6 7,12 10,18 6,12 46,57 58,49 4,-3 5,-13 6,-17 2,-7 5,-10 6,-18 2,-8 1,-12 4,-19 4,-11 3,-8 3,-20zm50 -125c0,38 14,39 18,77 1,15 12,49 26,61 21,18 40,5 59,-12 15,-13 22,-29 25,-32 15,-18 4,-3 12,-18l12 -17c12,-20 22,-47 22,-70 0,-7 -6,-26 -10,-31 -15,-20 -9,-10 -21,-18 -13,-10 -30,-12 -44,-15 -32,-6 -55,-6 -77,16 -12,12 -22,36 -22,59z"/>
30
+  </g>
31
+ </g>
32
+</svg>
Back to file index

layer.yaml

 1
--- 
 2
+++ layer.yaml
 3
@@ -0,0 +1,17 @@
 4
+"options":
 5
+  "basic":
 6
+    "packages":
 7
+    - "unzip"
 8
+    - "tar"
 9
+    - "binutils"
10
+    "use_venv": !!bool "false"
11
+    "include_system_packages": !!bool "false"
12
+  "ibm-mobilefirst-server": {}
13
+  "ibm-im": {}
14
+"repo": "bzr+ssh://bazaar.launchpad.net/~ibmcharmers/charms/trusty/layer-ibm-mobilefirst-server/trunk/"
15
+"includes":
16
+- "layer:basic"
17
+- "layer:ibm-im"
18
+- "interface:db2"
19
+- "interface:http"
20
+"is": "ibm-mobilefirst-server"
Back to file index

lib/charms/layer/__init__.py

 1
--- 
 2
+++ lib/charms/layer/__init__.py
 3
@@ -0,0 +1,21 @@
 4
+import os
 5
+
 6
+
 7
+class LayerOptions(dict):
 8
+    def __init__(self, layer_file, section=None):
 9
+        import yaml  # defer, might not be available until bootstrap
10
+        with open(layer_file) as f:
11
+            layer = yaml.safe_load(f.read())
12
+        opts = layer.get('options', {})
13
+        if section and section in opts:
14
+            super(LayerOptions, self).__init__(opts.get(section))
15
+        else:
16
+            super(LayerOptions, self).__init__(opts)
17
+
18
+
19
+def options(section=None, layer_file=None):
20
+    if not layer_file:
21
+        base_dir = os.environ.get('CHARM_DIR', os.getcwd())
22
+        layer_file = os.path.join(base_dir, 'layer.yaml')
23
+
24
+    return LayerOptions(layer_file, section)
Back to file index

lib/charms/layer/basic.py

  1
--- 
  2
+++ lib/charms/layer/basic.py
  3
@@ -0,0 +1,196 @@
  4
+import os
  5
+import sys
  6
+import shutil
  7
+from glob import glob
  8
+from subprocess import check_call
  9
+
 10
+from charms.layer.execd import execd_preinstall
 11
+
 12
+
 13
+def lsb_release():
 14
+    """Return /etc/lsb-release in a dict"""
 15
+    d = {}
 16
+    with open('/etc/lsb-release', 'r') as lsb:
 17
+        for l in lsb:
 18
+            k, v = l.split('=')
 19
+            d[k.strip()] = v.strip()
 20
+    return d
 21
+
 22
+
 23
+def bootstrap_charm_deps():
 24
+    """
 25
+    Set up the base charm dependencies so that the reactive system can run.
 26
+    """
 27
+    # execd must happen first, before any attempt to install packages or
 28
+    # access the network, because sites use this hook to do bespoke
 29
+    # configuration and install secrets so the rest of this bootstrap
 30
+    # and the charm itself can actually succeed. This call does nothing
 31
+    # unless the operator has created and populated $CHARM_DIR/exec.d.
 32
+    execd_preinstall()
 33
+    # ensure that $CHARM_DIR/bin is on the path, for helper scripts
 34
+    os.environ['PATH'] += ':%s' % os.path.join(os.environ['CHARM_DIR'], 'bin')
 35
+    venv = os.path.abspath('../.venv')
 36
+    vbin = os.path.join(venv, 'bin')
 37
+    vpip = os.path.join(vbin, 'pip')
 38
+    vpy = os.path.join(vbin, 'python')
 39
+    if os.path.exists('wheelhouse/.bootstrapped'):
 40
+        activate_venv()
 41
+        return
 42
+    # bootstrap wheelhouse
 43
+    if os.path.exists('wheelhouse'):
 44
+        with open('/root/.pydistutils.cfg', 'w') as fp:
 45
+            # make sure that easy_install also only uses the wheelhouse
 46
+            # (see https://github.com/pypa/pip/issues/410)
 47
+            charm_dir = os.environ['CHARM_DIR']
 48
+            fp.writelines([
 49
+                "[easy_install]\n",
 50
+                "allow_hosts = ''\n",
 51
+                "find_links = file://{}/wheelhouse/\n".format(charm_dir),
 52
+            ])
 53
+        apt_install([
 54
+            'python3-pip',
 55
+            'python3-setuptools',
 56
+            'python3-yaml',
 57
+            'python3-dev',
 58
+        ])
 59
+        from charms import layer
 60
+        cfg = layer.options('basic')
 61
+        # include packages defined in layer.yaml
 62
+        apt_install(cfg.get('packages', []))
 63
+        # if we're using a venv, set it up
 64
+        if cfg.get('use_venv'):
 65
+            if not os.path.exists(venv):
 66
+                series = lsb_release()['DISTRIB_CODENAME']
 67
+                if series in ('precise', 'trusty'):
 68
+                    apt_install(['python-virtualenv'])
 69
+                else:
 70
+                    apt_install(['virtualenv'])
 71
+                cmd = ['virtualenv', '-ppython3', '--never-download', venv]
 72
+                if cfg.get('include_system_packages'):
 73
+                    cmd.append('--system-site-packages')
 74
+                check_call(cmd)
 75
+            os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
 76
+            pip = vpip
 77
+        else:
 78
+            pip = 'pip3'
 79
+            # save a copy of system pip to prevent `pip3 install -U pip`
 80
+            # from changing it
 81
+            if os.path.exists('/usr/bin/pip'):
 82
+                shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save')
 83
+        # need newer pip, to fix spurious Double Requirement error:
 84
+        # https://github.com/pypa/pip/issues/56
 85
+        check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse',
 86
+                    'pip'])
 87
+        # install the rest of the wheelhouse deps
 88
+        check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] +
 89
+                   glob('wheelhouse/*'))
 90
+        if not cfg.get('use_venv'):
 91
+            # restore system pip to prevent `pip3 install -U pip`
 92
+            # from changing it
 93
+            if os.path.exists('/usr/bin/pip.save'):
 94
+                shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip')
 95
+                os.remove('/usr/bin/pip.save')
 96
+        os.remove('/root/.pydistutils.cfg')
 97
+        # flag us as having already bootstrapped so we don't do it again
 98
+        open('wheelhouse/.bootstrapped', 'w').close()
 99
+        # Ensure that the newly bootstrapped libs are available.
100
+        # Note: this only seems to be an issue with namespace packages.
101
+        # Non-namespace-package libs (e.g., charmhelpers) are available
102
+        # without having to reload the interpreter. :/
103
+        reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
104
+
105
+
106
+def activate_venv():
107
+    """
108
+    Activate the venv if enabled in ``layer.yaml``.
109
+
110
+    This is handled automatically for normal hooks, but actions might
111
+    need to invoke this manually, using something like:
112
+
113
+        # Load modules from $CHARM_DIR/lib
114
+        import sys
115
+        sys.path.append('lib')
116
+
117
+        from charms.layer.basic import activate_venv
118
+        activate_venv()
119
+
120
+    This will ensure that modules installed in the charm's
121
+    virtual environment are available to the action.
122
+    """
123
+    venv = os.path.abspath('../.venv')
124
+    vbin = os.path.join(venv, 'bin')
125
+    vpy = os.path.join(vbin, 'python')
126
+    from charms import layer
127
+    cfg = layer.options('basic')
128
+    if cfg.get('use_venv') and '.venv' not in sys.executable:
129
+        # activate the venv
130
+        os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
131
+        reload_interpreter(vpy)
132
+
133
+
134
+def reload_interpreter(python):
135
+    """
136
+    Reload the python interpreter to ensure that all deps are available.
137
+
138
+    Newly installed modules in namespace packages sometimes seemt to
139
+    not be picked up by Python 3.
140
+    """
141
+    os.execle(python, python, sys.argv[0], os.environ)
142
+
143
+
144
+def apt_install(packages):
145
+    """
146
+    Install apt packages.
147
+
148
+    This ensures a consistent set of options that are often missed but
149
+    should really be set.
150
+    """
151
+    if isinstance(packages, (str, bytes)):
152
+        packages = [packages]
153
+
154
+    env = os.environ.copy()
155
+
156
+    if 'DEBIAN_FRONTEND' not in env:
157
+        env['DEBIAN_FRONTEND'] = 'noninteractive'
158
+
159
+    cmd = ['apt-get',
160
+           '--option=Dpkg::Options::=--force-confold',
161
+           '--assume-yes',
162
+           'install']
163
+    check_call(cmd + packages, env=env)
164
+
165
+
166
+def init_config_states():
167
+    import yaml
168
+    from charmhelpers.core import hookenv
169
+    from charms.reactive import set_state
170
+    from charms.reactive import toggle_state
171
+    config = hookenv.config()
172
+    config_defaults = {}
173
+    config_defs = {}
174
+    config_yaml = os.path.join(hookenv.charm_dir(), 'config.yaml')
175
+    if os.path.exists(config_yaml):
176
+        with open(config_yaml) as fp:
177
+            config_defs = yaml.safe_load(fp).get('options', {})
178
+            config_defaults = {key: value.get('default')
179
+                               for key, value in config_defs.items()}
180
+    for opt in config_defs.keys():
181
+        if config.changed(opt):
182
+            set_state('config.changed')
183
+            set_state('config.changed.{}'.format(opt))
184
+        toggle_state('config.set.{}'.format(opt), config.get(opt))
185
+        toggle_state('config.default.{}'.format(opt),
186
+                     config.get(opt) == config_defaults[opt])
187
+    hookenv.atexit(clear_config_states)
188
+
189
+
190
+def clear_config_states():
191
+    from charmhelpers.core import hookenv, unitdata
192
+    from charms.reactive import remove_state
193
+    config = hookenv.config()
194
+    remove_state('config.changed')
195
+    for opt in config.keys():
196
+        remove_state('config.changed.{}'.format(opt))
197
+        remove_state('config.set.{}'.format(opt))
198
+        remove_state('config.default.{}'.format(opt))
199
+    unitdata.kv().flush()
Back to file index

lib/charms/layer/execd.py

  1
--- 
  2
+++ lib/charms/layer/execd.py
  3
@@ -0,0 +1,138 @@
  4
+# Copyright 2014-2016 Canonical Limited.
  5
+#
  6
+# This file is part of layer-basic, the reactive base layer for Juju.
  7
+#
  8
+# charm-helpers is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU Lesser General Public License version 3 as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# charm-helpers is distributed in the hope that it will be useful,
 13
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
 14
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15
+# GNU Lesser General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU Lesser General Public License
 18
+# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+# This module may only import from the Python standard library.
 21
+import os
 22
+import sys
 23
+import subprocess
 24
+import time
 25
+
 26
+'''
 27
+execd/preinstall
 28
+
 29
+It is often necessary to configure and reconfigure machines
 30
+after provisioning, but before attempting to run the charm.
 31
+Common examples are specialized network configuration, enabling
 32
+of custom hardware, non-standard disk partitioning and filesystems,
 33
+adding secrets and keys required for using a secured network.
 34
+
 35
+The reactive framework's base layer invokes this mechanism as
 36
+early as possible, before any network access is made or dependencies
 37
+unpacked or non-standard modules imported (including the charms.reactive
 38
+framework itself).
 39
+
 40
+Operators needing to use this functionality may branch a charm and
 41
+create an exec.d directory in it. The exec.d directory in turn contains
 42
+one or more subdirectories, each of which contains an executable called
 43
+charm-pre-install and any other required resources. The charm-pre-install
 44
+executables are run, and if successful, state saved so they will not be
 45
+run again.
 46
+
 47
+    $CHARM_DIR/exec.d/mynamespace/charm-pre-install
 48
+
 49
+An alternative to branching a charm is to compose a new charm that contains
 50
+the exec.d directory, using the original charm as a layer,
 51
+
 52
+A charm author could also abuse this mechanism to modify the charm
 53
+environment in unusual ways, but for most purposes it is saner to use
 54
+charmhelpers.core.hookenv.atstart().
 55
+'''
 56
+
 57
+
 58
+def default_execd_dir():
 59
+    return os.path.join(os.environ['CHARM_DIR'], 'exec.d')
 60
+
 61
+
 62
+def execd_module_paths(execd_dir=None):
 63
+    """Generate a list of full paths to modules within execd_dir."""
 64
+    if not execd_dir:
 65
+        execd_dir = default_execd_dir()
 66
+
 67
+    if not os.path.exists(execd_dir):
 68
+        return
 69
+
 70
+    for subpath in os.listdir(execd_dir):
 71
+        module = os.path.join(execd_dir, subpath)
 72
+        if os.path.isdir(module):
 73
+            yield module
 74
+
 75
+
 76
+def execd_submodule_paths(command, execd_dir=None):
 77
+    """Generate a list of full paths to the specified command within exec_dir.
 78
+    """
 79
+    for module_path in execd_module_paths(execd_dir):
 80
+        path = os.path.join(module_path, command)
 81
+        if os.access(path, os.X_OK) and os.path.isfile(path):
 82
+            yield path
 83
+
 84
+
 85
+def execd_sentinel_path(submodule_path):
 86
+    module_path = os.path.dirname(submodule_path)
 87
+    execd_path = os.path.dirname(module_path)
 88
+    module_name = os.path.basename(module_path)
 89
+    submodule_name = os.path.basename(submodule_path)
 90
+    return os.path.join(execd_path,
 91
+                        '.{}_{}.done'.format(module_name, submodule_name))
 92
+
 93
+
 94
+def execd_run(command, execd_dir=None, stop_on_error=True, stderr=None):
 95
+    """Run command for each module within execd_dir which defines it."""
 96
+    if stderr is None:
 97
+        stderr = sys.stdout
 98
+    for submodule_path in execd_submodule_paths(command, execd_dir):
 99
+        # Only run each execd once. We cannot simply run them in the
100
+        # install hook, as potentially storage hooks are run before that.
101
+        # We cannot rely on them being idempotent.
102
+        sentinel = execd_sentinel_path(submodule_path)
103
+        if os.path.exists(sentinel):
104
+            continue
105
+
106
+        try:
107
+            subprocess.check_call([submodule_path], stderr=stderr,
108
+                                  universal_newlines=True)
109
+            with open(sentinel, 'w') as f:
110
+                f.write('{} ran successfully {}\n'.format(submodule_path,
111
+                                                          time.ctime()))
112
+                f.write('Removing this file will cause it to be run again\n')
113
+        except subprocess.CalledProcessError as e:
114
+            # Logs get the details. We can't use juju-log, as the
115
+            # output may be substantial and exceed command line
116
+            # length limits.
117
+            print("ERROR ({}) running {}".format(e.returncode, e.cmd),
118
+                  file=stderr)
119
+            print("STDOUT<<EOM", file=stderr)
120
+            print(e.output, file=stderr)
121
+            print("EOM", file=stderr)
122
+
123
+            # Unit workload status gets a shorter fail message.
124
+            short_path = os.path.relpath(submodule_path)
125
+            block_msg = "Error ({}) running {}".format(e.returncode,
126
+                                                       short_path)
127
+            try:
128
+                subprocess.check_call(['status-set', 'blocked', block_msg],
129
+                                      universal_newlines=True)
130
+                if stop_on_error:
131
+                    sys.exit(0)  # Leave unit in blocked state.
132
+            except Exception:
133
+                pass  # We care about the exec.d/* failure, not status-set.
134
+
135
+            if stop_on_error:
136
+                sys.exit(e.returncode or 1)  # Error state for pre-1.24 Juju
137
+
138
+
139
+def execd_preinstall(execd_dir=None):
140
+    """Run charm-pre-install for each module within execd_dir."""
141
+    execd_run('charm-pre-install', execd_dir=execd_dir)
Back to file index

metadata.yaml

 1
--- 
 2
+++ metadata.yaml
 3
@@ -0,0 +1,32 @@
 4
+"name": "ibm-mobilefirst-server"
 5
+"summary": "IBM MobileFirst Server"
 6
+"maintainer": "IBM Juju Support team <jujusupp@us.ibm.com>"
 7
+"description": |
 8
+  IBM MobileFirst Server is mobile-optimized middleware that serves as a gateway between applications, back-end systems and cloud-based services.
 9
+"tags":
10
+- "ibm"
11
+- "im"
12
+- "misc"
13
+"requires":
14
+  "mobilefirstserverdb":
15
+    "interface": "db2"
16
+  "website":
17
+    "interface": "http"
18
+    "scope": "container"
19
+"resources":
20
+  "ibm_im_installer":
21
+    "type": "file"
22
+    "filename": "ibm_im_installer.zip"
23
+    "description": "Installation Manager installer archive"
24
+  "ibm_im_fixpack":
25
+    "type": "file"
26
+    "filename": "ibm_im_fixpack.zip"
27
+    "description": "Installation Manager fix pack archive"
28
+  "ibm_mobilefirstserver_installer":
29
+    "type": "file"
30
+    "filename": "ibm_worklight_installer.zip"
31
+    "description": "IBM Mobile First Server Package"
32
+"subordinate": !!bool "true"
33
+"terms":
34
+- "ibm-im/1"
35
+- "ibm-mobilefirstserver/1"
Back to file index

reactive/ibm-im.sh

  1
--- 
  2
+++ reactive/ibm-im.sh
  3
@@ -0,0 +1,187 @@
  4
+#!/bin/bash
  5
+set -e
  6
+
  7
+source charms.reactive.sh
  8
+ARCHITECTURE=`uname -m`
  9
+
 10
+# Installation Manager install path
 11
+IM_INSTALL_PATH="/opt/IBM/InstallationManager"
 12
+
 13
+@when_not 'ibm-im.installed'
 14
+function install_ibm_im(){
 15
+      # Fail fast if we're on an unsupported arch
 16
+      if [ "$ARCHITECTURE" != "x86_64" -a  "$ARCHITECTURE" != "ppc64le" -a "$ARCHITECTURE" != "s390x" ]; then
 17
+            juju-log "IBM IM: only supported on x86_64 or ppc64le or s390x platforms"
 18
+            status-set blocked "unsupported architecture"
 19
+            return 1
 20
+      fi
 21
+
 22
+      # Get the installable resource
 23
+      juju-log "IBM IM: fetching the ibm_im_installer resource"
 24
+      status-set maintenance "fetching the ibm_im_installer resource"
 25
+      cfg_im_pkg_name=`resource-get 'ibm_im_installer' || echo unavailable`
 26
+
 27
+      # If we don't have a package, report blocked status; we can't proceed.
 28
+      if [ "$cfg_im_pkg_name" = "unavailable" ]; then
 29
+        juju-log "IBM IM: missing required ibm_im_installer resource"
 30
+        status-set blocked "missing required ibm_im_installer resource"
 31
+        return 0
 32
+      fi
 33
+
 34
+      juju-log "IBM IM: using $cfg_im_pkg_name as the ibm_im_installer resource"
 35
+      ARCHIVE_DIR=`dirname $cfg_im_pkg_name`
 36
+
 37
+      # Extract the installer contents
 38
+      if [ -f  $cfg_im_pkg_name ]; then
 39
+           juju-log "IBM IM: extracting the ibm_im_installer resource"
 40
+           status-set maintenance "extracting the ibm_im_installer resource"
 41
+           if [ -f $ARCHIVE_DIR/userinstc ]; then
 42
+                 juju-log "IBM IM: ibm_im_installer resource already extracted"
 43
+           else
 44
+                 cd $ARCHIVE_DIR
 45
+                 if ! unzip $cfg_im_pkg_name; then
 46
+                      juju-log "IBM IM: Unable to extract the ibm_im_installer resource"
 47
+                      # Remove corrupt archive file
 48
+                      status-set blocked "ibm_im_installer resource is corrupt"
 49
+                      rm -rf $ARCHIVE_DIR
 50
+                      return 0
 51
+                 else
 52
+                      juju-log "IBM IM: ibm_im_installer resource extracted successfully"
 53
+                 fi
 54
+           fi
 55
+      fi
 56
+
 57
+      # Do the actual IBM IM install
 58
+      if [ -f  $ARCHIVE_DIR/userinstc ]; then
 59
+        juju-log "IBM IM: starting installation."
 60
+        status-set maintenance "installing ibm-im"
 61
+        cp $ARCHIVE_DIR/install.xml $ARCHIVE_DIR/silent_install.xml
 62
+        sed -i "2 a \<profile kind='self' installLocation='$IM_INSTALL_PATH' id='IBM Installation Manager'>\n <data key='eclipseLocation' value='$IM_INSTALL_PATH' />\n</profile>" $ARCHIVE_DIR/silent_install.xml
 63
+        if $ARCHIVE_DIR/userinstc -input $ARCHIVE_DIR/silent_install.xml -acceptlicense; then
 64
+            set_state 'ibm-im.installed'
 65
+            status-set active "ready"
 66
+        else
 67
+            juju-log "IBM IM: error while installing"
 68
+            return 1
 69
+        fi
 70
+      else
 71
+        juju-log "IBM IM: installer was not found."
 72
+        status-set blocked "installation failed"
 73
+        return 1
 74
+      fi
 75
+}
 76
+
 77
+@when 'ibm-im.installed'
 78
+@when_not 'ibm-im.updated'
 79
+function install_ibm_im_fixpack(){
 80
+    # Get the fixpack resource
 81
+    juju-log "IBM IM: fetching the ibm_im_fixpack resource"
 82
+    status-set maintenance "fetching the ibm_im_fixpack resource"
 83
+    ibm_im_fp_package=`resource-get 'ibm_im_fixpack' || echo unavailable`
 84
+
 85
+    # If we don't have a fixpack, just exit successfully; there's nothing to do.
 86
+    if [ "$ibm_im_fp_package" = "unavailable" ]; then
 87
+      juju-log "IBM IM: no ibm_im_fixpack to install"
 88
+      status-set active "ready"
 89
+      return 0
 90
+    fi
 91
+
 92
+    # Currently, there is no way to make a resource optional, so something
 93
+    # must be uploaded to the charm store, even if it's a dummy archive.
 94
+    # If we detect the fixpack is just a placeholder (because we created an
 95
+    # empty placeholder in the store), exit just like we do with a missing
 96
+    # fixpack resource.
 97
+    ibm_im_fp_empty=`file $ibm_im_fp_package | { grep -q empty && echo "True"; } || echo "False"`
 98
+    if [ "$ibm_im_fp_empty" = "True" ]; then
 99
+      juju-log "IBM IM: no ibm_im_fixpack to install"
100
+      status-set active "ready"
101
+      return 0
102
+    fi
103
+
104
+    juju-log "IBM IM: using $ibm_im_fp_package as the ibm_im_fixpack resource"
105
+    ARCHIVE_DIR=`dirname $ibm_im_fp_package`
106
+
107
+    # Extract the fixpack contents
108
+    if [ -f $ibm_im_fp_package ]; then
109
+        juju-log "IBM IM: extracting the ibm_im_fixpack resource"
110
+        status-set maintenance "extracting the ibm_im_fixpack resource"
111
+        if [ -f $ARCHIVE_DIR/userinstc ]; then
112
+              juju-log "IBM IM: ibm_im_fixpack resource already extracted"
113
+        else
114
+              cd $ARCHIVE_DIR
115
+              if ! unzip $ibm_im_fp_package; then
116
+                      juju-log "IBM IM: Unable to extract the ibm_im_fixpack resource"
117
+                      # Remove corrupt archive file
118
+                      status-set blocked "ibm_im_fixpack resource is corrupt"
119
+                      rm  -rf $ARCHIVE_DIR
120
+                      return 0
121
+              else
122
+                      juju-log "IBM IM: ibm_im_fixpack resource extracted successfully"
123
+              fi
124
+        fi
125
+    fi
126
+
127
+    # Do the actual IBM IM fixpack install
128
+    if [ -f $ARCHIVE_DIR/userinstc ]; then
129
+        juju-log "IBM IM: starting fixpack installation."
130
+        status-set maintenance "installing fixpack"
131
+        cp $ARCHIVE_DIR/install.xml $ARCHIVE_DIR/silent_install.xml
132
+        sed -i "2 a \<profile kind='self' installLocation='$IM_INSTALL_PATH' id='IBM Installation Manager'>\n <data key='eclipseLocation' value='$IM_INSTALL_PATH' />\n</profile>" $ARCHIVE_DIR/silent_install.xml
133
+        if $ARCHIVE_DIR/userinstc -input $ARCHIVE_DIR/silent_install.xml -acceptlicense; then
134
+             set_state 'ibm-im.updated'
135
+             status-set active "ready"
136
+        else
137
+             juju-log "IBM IM: error while installing the fixpack."
138
+             return 1
139
+        fi
140
+    else
141
+        juju-log "IBM_IM: Fix pack installer was not found."
142
+        status-set blocked "fixpack installation failed"
143
+        return 1
144
+   fi
145
+}
146
+
147
+@hook 'upgrade-charm'
148
+function check_fixpack(){
149
+    # The upgrade-charm hook will fire when a new resource is pushed for this
150
+    # charm. This is a good time to determine if we need to deal with a new
151
+    # fixpack.
152
+    if ! charms.reactive is_state 'ibm-im.updated'; then
153
+        # If there is no prior fixpack installed (because ibm-im.updated is not
154
+        # set), do nothing since install-ibm-im-fixpack will handle that case.
155
+        juju-log "IBM IM: no fixpack has been installed; nothing to upgrade."
156
+        return 0
157
+    else
158
+        # If we have a fixpack already (because ibm-im.updated is set),
159
+        # we should fetch the latest fixpack and determine if it is new.
160
+        #  - If it is new, set our states so install-ibm-im-fixpack is called again
161
+        #  - If it is not new, do nothing
162
+        juju-log "IBM IM: scanning for new fixpacks to install"
163
+        ARCHIVE_DIR="$CHARM_DIR/../resources/ibm_im_fixpack"
164
+        CUR_FIXPACK="$ARCHIVE_DIR/ibm_im_fixpack.zip"
165
+
166
+        # Sum any existing fixpack to determine if we have a new one
167
+        if [ -f $CUR_FIXPACK ]; then
168
+            CUR_MD5=`md5sum "$CUR_FIXPACK" | awk '{print $1}'`
169
+            # Calling resource-get here will fetch the fixpack resource.
170
+            NEW_FIXPACK=`resource-get 'ibm_im_fixpack' || echo unavailable`
171
+            if [ "$NEW_FIXPACK" = "unavailable" ]; then
172
+                juju-log "IBM IM: no fixpack to install"
173
+            else
174
+                NEW_MD5=`md5sum "$NEW_FIXPACK" | awk '{print $1}'`
175
+                # If sums don't match, we have a new fp. Configure states so
176
+                # we re-run install-ibm-im-fixpack().
177
+                if [ "$CUR_MD5" != "$NEW_MD5" ]; then
178
+                    juju-log "IBM IM: new fixpack detected ($CUR_FIXPACK with $CUR_MD5 versus $NEW_FIXPACK with $NEW_MD5)"
179
+                    rm -rf $ARCHIVE_DIR
180
+                    remove_state 'ibm-im.updated'
181
+                else
182
+                    juju-log "IBM IM: no new fixpack to install"
183
+                fi
184
+            fi
185
+        fi
186
+    fi
187
+}
188
+
189
+
190
+reactive_handler_main
Back to file index

reactive/ibm-mobilefirst-server.sh

  1
--- 
  2
+++ reactive/ibm-mobilefirst-server.sh
  3
@@ -0,0 +1,542 @@
  4
+#!/bin/bash
  5
+set -ex
  6
+
  7
+source charms.reactive.sh
  8
+
  9
+ARCHITECTURE=`uname -m`
 10
+ARCHIVE_DIR=$CHARM_DIR/files/archives
 11
+AUTH_KEY_FILE=/root/.ssh/authorized_keys
 12
+IM_INSTALL_PATH=/opt/IBM/InstallationManager
 13
+LIBERTY_INSTALL_PATH=/opt/ibm/wlp
 14
+MobileFirstServer_PATH=/opt/ibm/MobileFirst_Platform_Server_liberty
 15
+
 16
+
 17
+# Create a workight server.
 18
+create_worklightserver()
 19
+{
 20
+if [ -d $LIBERTY_INSTALL_PATH/bin ]; then
 21
+	juju-log "IBM MFS: Creating worklight server"
 22
+	#Create worklight server
 23
+	if [ -d $LIBERTY_INSTALL_PATH/usr/servers/worklight ]; then
 24
+		juju-log "IBM MFS: server is already there"
 25
+	else
 26
+		$LIBERTY_INSTALL_PATH/bin/server create worklight
 27
+		juju-log "IBM MFS: created server sucessfully"
 28
+		cd $LIBERTY_INSTALL_PATH/usr/servers
 29
+		juju-log "IBM MFS: Editing http port to $httpport and https port to $httpsport in server.xml"
 30
+		sed -i 's/9080/'$1'/' $LIBERTY_INSTALL_PATH/usr/servers/worklight/server.xml
 31
+		sed -i 's/9443/'$2'/' $LIBERTY_INSTALL_PATH/usr/servers/worklight/server.xml
 32
+	fi
 33
+	return 0
 34
+else
 35
+	juju-log "IBM MFS: Websphere Liberty is not installed"
 36
+	return 1
 37
+fi
 38
+
 39
+}
 40
+remove_worklightserver()
 41
+{
 42
+	if [ -d $LIBERTY_INSTALL_PATH/usr/servers/worklight ]; then
 43
+		juju-log "IBM MFS: server is already there"
 44
+		sudo rm -rf $LIBERTY_INSTALL_PATH/usr/servers/worklight
 45
+		juju-log "IBM MFS: Worklight Server deleted"
 46
+	fi
 47
+}
 48
+#Clean the old installations if any
 49
+clean_old_installations()
 50
+{
 51
+	juju-log "Clean old installations if any before installations"
 52
+	#Uninstall if already present
 53
+	if [ -d $MobileFirstServer_PATH/WorklightServer ]; then
 54
+		juju-log "Editing uninstall response file with charmdir"
 55
+		sed -i 's|/root/Installfiles/MobileFirst_Platform_Server/disk1|'$CHARM_DIR'/files/archives/MobileFirst_Platform_Server/disk1|g' $CHARM_DIR/files/archives/uninstall-worklight-responsefile.xml
 56
+		cd $IM_INSTALL_PATH/eclipse/tools
 57
+		sudo ./imcl input $CHARM_DIR/files/archives/uninstall-worklight-responsefile.xml
 58
+		rm -rf $MobileFirstServer_PATH
 59
+		juju-log "IBM MFS: MobileFirst Server software uninstalled"
 60
+
 61
+	fi
 62
+
 63
+
 64
+}
 65
+
 66
+get_db_values_from_interface()
 67
+{
 68
+	db2_port=$(relation_call --state=mobilefirstserverdb.ready get_db2_port) || true
 69
+	db2_instance=$(relation_call --state=mobilefirstserverdb.ready get_db2_instance_name) || true
 70
+	db2_path=$(relation_call --state=mobilefirstserverdb.ready get_db2_path) || true
 71
+	hostname=$(relation_call --state=mobilefirstserverdb.ready get_db2_hostname) || true
 72
+	dbusername=$(relation_call --state=mobilefirstserverdb.ready get_dbusername) || true
 73
+	dbuserpw=$(relation_call --state=mobilefirstserverdb.ready get_dbuserpw) || true
 74
+	dbs_created=$(relation_call --state=mobilefirstserverdb.ready get_db2_dbnames) || true
 75
+
 76
+}
 77
+
 78
+#Edit MobileFirst Server install responsefile with Liberty and db2 values
 79
+edit_responsefile()
 80
+{
 81
+	juju-log "IBM MFS: Editing response file"
 82
+	get_db_values_from_interface
 83
+
 84
+	PAK_DIR="$CHARM_DIR/../resources/ibm_mobilefirstserver_installer"
 85
+	PAK_NAME="$PAK_DIR/ibm_worklight_installer.zip"
 86
+	sed -i 's|/root/IBM/IBMIMShared|/opt/ibm/IBMIMShared|g' $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 87
+	sed -i 's|/root/Installfiles/MobileFirst_Platform_Server/disk1|'$PAK_DIR'/MobileFirst_Platform_Server/disk1|g' $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 88
+	sed -i 's/APPCNTRA/APPCNTR/' $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 89
+	sed -i 's/50000/'$db2_port'/' $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 90
+	sed -i 's/db2inst1/'$dbusername'/' $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 91
+	sed -i 's/localhost/'$hostname'/' $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 92
+	oldpasswd=`cat $CHARM_DIR/files/archives/install-MFS-responsefile.xml | grep "user.database.db2.appcenter.password" | cut -d"=" -f3 | cut -d"/" -f1`
 93
+	oldpasswd=`echo $oldpasswd | tr -d "'"`
 94
+	sed -i s/$oldpasswd/$dbuserpw/g $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 95
+	
 96
+	sed -i 's|/opt/ibm/db2/V10.5/java/db2jcc.jar|'$CHARM_DIR'/files/db2jcc4.jar|g' $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 97
+	sed -i 's/waserver1/worklight/' $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 98
+	sed -i 's|/root/IBM/WebSphere/wlp|'$LIBERTY_INSTALL_PATH'|g' $CHARM_DIR/files/archives/install-MFS-responsefile.xml
 99
+	juju-log "IBM MFS:Response file edited"
100
+}
101
+
102
+#Edit configure_liberty_db2_file
103
+edit_configure_liberty_db2_file()
104
+{
105
+	juju-log "IBM MFS: Editing configure_liberty_db2.xml file"
106
+	get_db_values_from_interface
107
+
108
+	cp $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2-orig.xml $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
109
+
110
+	sed -i '/"database.db2.wladmin.password"/c\<property name="database.db2.wladmin.password" value="'$dbuserpw'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
111
+	sed -i '/"database.db2.worklight.password"/c\<property name="database.db2.worklight.password" value="'$dbuserpw'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
112
+	sed -i '/"database.db2.worklightreports.password"/c\<property name="database.db2.worklightreports.password" value="'$dbuserpw'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
113
+	sed -i '/"worklight.server.install.dir"/c\<property name="worklight.server.install.dir" value="'$MobileFirstServer_PATH'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
114
+	sed -i '/"worklight.contextroot"/c\<property name="worklight.contextroot" value="/worklight"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
115
+	sed -i '/"database.db2.host"/c\<property name="database.db2.host" value="'$hostname'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
116
+	sed -i '/"database.db2.port"/c\<property name="database.db2.port" value="'$db2_port'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
117
+	sed -i '/"database.db2.instance"/c\<property name="database.db2.instance" value="'$db2_instance'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
118
+	sed -i '/"database.db2.driver.dir"/c\<property name="database.db2.driver.dir" value="'$CHARM_DIR'/files"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
119
+	sed -i '/"database.db2.wladmin.dbname"/c\<property name="database.db2.wladmin.dbname" value="WRKLGHT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
120
+	sed -i '/"database.db2.wladmin.schema"/c\<property name="database.db2.wladmin.schema" value="WLADMIN"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
121
+	sed -i '/"database.db2.wladmin.username"/c\<property name="database.db2.wladmin.username" value="'$dbusername'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
122
+	sed -i '/"database.db2.worklight.dbname"/c\<property name="database.db2.worklight.dbname" value="WRKLGHT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
123
+	sed -i '/"database.db2.worklight.schema"/c\<property name="database.db2.worklight.schema" value="WRKLGHT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
124
+	sed -i '/"database.db2.worklight.username"/c\<property name="database.db2.worklight.username" value="'$dbusername'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
125
+	sed -i '/"database.db2.worklightreports.dbname"/c\<property name="database.db2.worklightreports.dbname" value="WRKLGHT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
126
+	sed -i '/"database.db2.worklightreports.schema"/c\<property name="database.db2.worklightreports.schema" value="WLREPORT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
127
+	sed -i '/"database.db2.worklightreports.username"/c\<property name="database.db2.worklightreports.username" value="'$dbusername'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
128
+	sed -i '/"appserver.was.installdir"/c\<property name="appserver.was.installdir" value="'$LIBERTY_INSTALL_PATH'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
129
+	sed -i '/"appserver.was85liberty.serverInstance"/c\<property name="appserver.was85liberty.serverInstance" value="worklight"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
130
+	sed -i '/"worklight.project.war.file"/c\<property name="worklight.project.war.file" value="'$CHARM_DIR'/files/archives/test.war"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml
131
+
132
+}
133
+update_usename_serverxml_file()
134
+{
135
+	password=`config-get worklight_passwd`
136
+	username=`config-get worklight_user`
137
+	juju-log "IBM MFS: Username is $username, Password is $password"
138
+	
139
+	juju-log "IBM MFS: Editing server.xml file for updated credentials"
140
+	cp $LIBERTY_INSTALL_PATH/usr/servers/worklight/server_org.xml $LIBERTY_INSTALL_PATH/usr/servers/worklight/server.xml
141
+
142
+	sed -i '/user name="admin"/c\<user name="'$username'" password="'$password'"/>' $LIBERTY_INSTALL_PATH/usr/servers/worklight/server.xml
143
+	sed -i '/member name="appcenteradmin"/c\<member name="'$username'"/>' $LIBERTY_INSTALL_PATH/usr/servers/worklight/server.xml
144
+}
145
+
146
+
147
+#Edit create_database_db2_file
148
+edit_create_database_db2_file()
149
+{
150
+	juju-log "IBM MFS: Editing create-database-db2.xml file"
151
+	get_db_values_from_interface
152
+
153
+
154
+	sed -i '/"database.db2.admin.password"/c\<property name="database.db2.admin.password" value="'$dbuserpw'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
155
+	sed -i '/"database.db2.wladmin.password"/c\<property name="database.db2.wladmin.password" value="'$dbuserpw'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
156
+	sed -i '/"database.db2.worklight.password"/c\<property name="database.db2.worklight.password" value="'$dbuserpw'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
157
+	sed -i '/"database.db2.worklightreports.password"/c\<property name="database.db2.worklightreports.password" value="'$dbuserpw'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
158
+	sed -i '/"worklight.server.install.dir"/c\<property name="worklight.server.install.dir" value="'$MobileFirstServer_PATH'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
159
+	sed -i '/"database.db2.host"/c\<property name="database.db2.host" value="'$hostname'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
160
+	sed -i '/"database.db2.port"/c\<property name="database.db2.port" value="'$db2_port'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
161
+	sed -i '/"database.db2.instance"/c\<property name="database.db2.instance" value="'$db2_instance'"/>'  $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
162
+	sed -i '/"database.db2.driver.dir"/c\<property name="database.db2.driver.dir" value="'$CHARM_DIR'/files"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
163
+	sed -i '/"database.db2.admin.username"/c\<property name="database.db2.admin.username" value="'$dbusername'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
164
+	sed -i '/"database.db2.wladmin.dbname"/c\<property name="database.db2.wladmin.dbname" value="WRKLGHT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
165
+	sed -i '/"database.db2.wladmin.schema"/c\<property name="database.db2.wladmin.schema" value="WLADMIN"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
166
+	sed -i '/"database.db2.wladmin.username"/c\<property name="database.db2.wladmin.username" value="'$dbusername'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
167
+	sed -i '/"database.db2.worklight.dbname"/c\<property name="database.db2.worklight.dbname" value="WRKLGHT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
168
+	sed -i '/"database.db2.worklight.schema"/c\<property name="database.db2.worklight.schema" value="WRKLGHT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
169
+	sed -i '/"database.db2.worklight.username"/c\<property name="database.db2.worklight.username" value="'$dbusername'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
170
+	sed -i '/"database.db2.worklightreports.dbname"/c\<property name="database.db2.worklightreports.dbname" value="WRKLGHT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
171
+	sed -i '/"database.db2.worklightreports.schema"/c\<property name="database.db2.worklightreports.schema" value="WLREPORT"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
172
+	sed -i '/"database.db2.worklightreports.username"/c\<property name="database.db2.worklightreports.username" value="'$dbusername'"/>' $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml
173
+}
174
+
175
+#Backup the configuration files to reuee it later it required.
176
+backup_configuration_files()
177
+{
178
+	cp $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2.xml $MobileFirstServer_PATH/WorklightServer/configuration-samples/configure-liberty-db2-orig.xml
179
+	cp $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2.xml $MobileFirstServer_PATH/WorklightServer/configuration-samples/create-database-db2-orig.xml
180
+}
181
+
182
+
183
+#Post install configurations.
184
+postworklightinstall_configuration()
185
+{
186
+
187
+	juju-log "IBM MFS: Editing configure-liberty-db2.xml file"
188
+	edit_configure_liberty_db2_file
189
+	juju-log "IBM MFS: Editing create-database-db2.xml file"
190
+	edit_create_database_db2_file
191
+
192
+	juju-log "IBM MFS: Running ant commands"
193
+	cd $MobileFirstServer_PATH/WorklightServer/configuration-samples
194
+	#Run create admdatabases command
195
+	ant -f create-database-db2.xml admdatabases
196
+	if [ $? -eq 0 ]; then
197
+		juju-log "IBM MFS: ant create admddatabase command ran successfully"
198
+	else
199
+		juju-log "IBM MFS: ant create admddatabase command failed to run"
200
+		return 1
201
+	fi
202
+
203
+	#Run create-database-db2.xml databases command
204
+	ant -f create-database-db2.xml databases
205
+	if [ $? -eq 0 ]; then
206
+		juju-log "IBM MFS: ant create database command ran successfully"
207
+	else
208
+		juju-log "IBM MFS: ant create database command failed to run"
209
+		return 1
210
+	fi
211
+
212
+	#Run configure admdatabases command
213
+	ant -f configure-liberty-db2.xml admdatabases
214
+	if [ $? -eq 0 ]; then
215
+		juju-log "IBM MFS: ant configure liberty admddatabase command ran successfully"
216
+	else
217
+		juju-log "IBM MFS: ant configure liberty admddatabase command failed to run"
218
+		return 1
219
+	fi
220
+	#Run configure databases command
221
+	ant -f configure-liberty-db2.xml databases
222
+	if [ $? -eq 0 ]; then
223
+		juju-log "IBM MFS: ant configure liberty database command ran successfully"
224
+	else
225
+		juju-log "IBM MFS: ant configure liberty database command failed to run"
226
+		return 1
227
+	fi
228
+
229
+	#Run adminstall command
230
+	ant -f configure-liberty-db2.xml adminstall
231
+	if [ $? -eq 0 ]; then
232
+		juju-log "IBM MFS: ant adminstall command ran successfully"
233
+	else
234
+		juju-log "IBM MFS: ant adminstall command failed to run"
235
+		return 1
236
+	fi
237
+
238
+	#Run install command to configure sample runtime environment
239
+	ant -f configure-liberty-db2.xml install
240
+	if [ $? -eq 0 ]; then
241
+		juju-log "IBM MFS: ant configure install command ran successfully"
242
+	else
243
+		juju-log "IBM MFS: ant configure install command failed to run"
244
+		return 1
245
+	fi
246
+
247
+}
248
+
249
+#Restart liberty server
250
+restart_worklightserver()
251
+{
252
+	if [ -d $LIBERTY_INSTALL_PATH/usr/servers/worklight ]; then
253
+		juju-log "IBM MFS: Restarting server"
254
+		set +e
255
+		$LIBERTY_INSTALL_PATH/bin/server stop worklight
256
+		if [ $? == 0 ]; then
257
+			juju-log "IBM MFS: Websphere Liberty stopped successfully"
258
+		else
259
+			juju-log "IBM MFS: Server not running"
260
+		fi
261
+
262
+		$LIBERTY_INSTALL_PATH/bin/server start worklight
263
+		if [ $? == 0 ]; then
264
+			juju-log "IBM MFS: Websphere Liberty started successfully"
265
+			set_state 'ibm-mobilefirst-server.website.started'
266
+		else
267
+			juju-log "IBM MFS: Server is running"
268
+			set_state 'ibm-mobilefirst-server.website.started'
269
+		fi
270
+
271
+	fi
272
+	juju-log "IBM MFS: server restarted successfully"
273
+
274
+}
275
+
276
+@when 'website.available'
277
+@when_not 'ibm-mobilefirst-server.website.configured'
278
+function capture_portdetials(){
279
+	juju-log "IBM MFS: Website is available"
280
+	#Get the port number from WLP and check whether the port is free and can be used. If not free, use the next one. 
281
+	hostname=`relation-get hostname`
282
+	juju-log "IBM MFS: Hostname is $hostname"
283
+	httpport=`relation-get port`
284
+	httpsport=`relation-get httpsport`
285
+	juju-log "IBM MFS: Http post = $httpport , Https port = $httpsport"
286
+	while [ 1 ]
287
+	do
288
+		result=`netstat -an | grep $httpport | cut -d"*" -f1`
289
+		if [ "$result" != "" ]; then
290
+			httpport=$((httpport+1))
291
+		else
292
+			break
293
+		fi
294
+	done;
295
+	while [ 1 ]
296
+	do
297
+		result=`netstat -an | grep $httpsport | cut -d"*" -f1`
298
+		if [ "$result" != "" ]; then
299
+			httpsport=$((httpsport+1))
300
+		else
301
+			break
302
+		fi
303
+	done;
304
+	echo "HttpPort:$httpport" > portdetails.txt
305
+	echo "HttpsPort:$httpsport" >> portdetails.txt
306
+
307
+	set_state 'ibm-mobilefirst-server.website.configured'
308
+	
309
+}
310
+
311
+@when 'website.available'
312
+@when 'ibm-mobilefirst-server.installed'
313
+@when_not 'ibm-mobilefirst-server.website.started'
314
+function start_worklight_server(){
315
+	if [ -d $LIBERTY_INSTALL_PATH/usr/servers/worklight ]; then
316
+
317
+		$LIBERTY_INSTALL_PATH/bin/server start worklight
318
+		if [ $? == 0 ]; then
319
+			juju-log "IBM MFS: Websphere Liberty started successfully"
320
+			set_state 'ibm-mobilefirst-server.website.started'
321
+		fi
322
+	fi
323
+}
324
+
325
+
326
+
327
+@when 'website.available'
328
+@when 'ibm-mobilefirst-server.website.configured'
329
+@when 'ibm-mobilefirst-server.mobilefirstserverdb.configured'
330
+@when_not 'ibm-mobilefirst-server.installed'
331
+function install() {
332
+	juju-log "IBM MFS: License accepted"
333
+	remove_state 'ibm-mobilefirst-server.uninstalled'
334
+
335
+	httpport=`cat portdetails.txt | grep "HttpPort" | cut -d":" -f2`
336
+	httpsport=`cat portdetails.txt | grep "HttpsPort" | cut -d":" -f2`
337
+	juju-log "httpport = $httpport $httpsport"
338
+	# Get the installable resource
339
+	juju-log "IBM MFS: Fetching the ibm_mobilefirst-server_installer resource"
340
+	status-set maintenance "fetching the ibm_mobilefirst-server_installer resource"
341
+	cfg_worklight_pkg_name=`resource-get 'ibm_mobilefirstserver_installer' || echo unavailable`
342
+	# If we don't have a package, report blocked status; we can't proceed.
343
+	if [ "$cfg_worklight_pkg_name" = "unavailable" ]; then
344
+		juju-log "IBM MFS: missing required ibm_mobilefirst-server_installer resource"
345
+		status-set blocked "missing required ibm_mobilefirst-server_installer resource"
346
+		exit 0
347
+	fi
348
+	# Check for empty packages from Charm store
349
+	ibm_worklight_empty=`file $cfg_worklight_pkg_name | { grep -q empty && echo "True"; } || echo "False"`
350
+	if [ "$ibm_worklight_empty" = "True" ]; then
351
+		juju-log "IBM MFS: No ibm_worklight package available to install"
352
+		#status-set active "ready"
353
+		status-set blocked "No ibm_worklight package available to install"
354
+		exit 0
355
+	fi
356
+
357
+	juju-log "IBM MFS: using $cfg_worklight_pkg_name as the ibm_mobilefirst-server_installer resource"
358
+	PKG_DIR=`dirname $cfg_worklight_pkg_name`
359
+	juju-log "Archive dir is $PKG_DIR"
360
+
361
+	cd $PKG_DIR
362
+	status-set maintenance "Extracting IBM MobileFirst Server package"
363
+	rm -rf MobileFirst_Platform_Server*
364
+	unzip $PKG_DIR/*.zip
365
+	if [ $? != 0 ]; then
366
+		juju-log "IBM MFS: Unable to extract the MFS package content. Verify whether the package is corrupt."
367
+		status-set blocked "IBM MobileFirst Server package is corrupt"
368
+		#Remove corrupt archive file
369
+		rm -rf $PKG_DIR/*.zip
370
+		return
371
+	fi
372
+	if [ -d $PKG_DIR/MobileFirst_Platform_Server/disk1 ]; then
373
+		juju-log "IBM MFS: MobileFirst server Packages are available for installation."
374
+		#Create worklight server
375
+		create_worklightserver $httpport $httpsport
376
+		if [ $? -eq 1 ] ; then
377
+			juju-log "IBM MFS: Websphere Liberty is not installed"
378
+			status-set blocked "Websphere Liberty is not installed"
379
+			return 0
380
+		fi
381
+		#Edit response file for installation
382
+		edit_responsefile
383
+		#edit_post_install_file
384
+		juju-log "IBM MFS: Installing MobileFirst Server package."
385
+		status-set maintenance "Installing IBM MobileFirst Server software"
386
+		cd $IM_INSTALL_PATH/eclipse/tools
387
+		./imcl input $CHARM_DIR/files/archives/install-MFS-responsefile.xml -acceptLicense -showProgress
388
+		juju-log "IBM MFS: Installation of MobileFirst server complete."
389
+		status-set active "Installed IBM MobileFirst Server"
390
+		#Check install successful or not
391
+		if [ -d $MobileFirstServer_PATH/WorklightServer ]; then
392
+			juju-log "IBM MFS: Install Sucessful and moving to post install configuration"
393
+			backup_configuration_files
394
+			status-set maintenance "Running Post install configuration steps for IBM MobileFirst Server"
395
+			postworklightinstall_configuration
396
+			if [ $? -eq 1 ]; then
397
+				juju-log "IBM MFS: Post Install Configuration script failed" 
398
+				return 1
399
+			fi
400
+			juju-log "IBM MFS: Post install configuration is done"
401
+			status-set maintenance "Post install configuration is done"
402
+			#Restart worklight server
403
+			restart_worklightserver
404
+			set_state 'ibm-mobilefirst-server.installed'
405
+			#Taking a backup of WLP server.xml file
406
+			cp $LIBERTY_INSTALL_PATH/usr/servers/worklight/server.xml $LIBERTY_INSTALL_PATH/usr/servers/worklight/server_org.xml
407
+			update_usename_serverxml_file
408
+			status-set active "IBM MFS Installed and is Ready"
409
+			open-port $httpport
410
+			open-port $httpsport
411
+
412
+		else
413
+			juju-log "IBM MFS: Install failed"
414
+			status-set blocked "IBM MobileFirst Server install failed"
415
+			exit 1
416
+		fi
417
+	else
418
+		juju-log "IBM MFS: MobileFirst server Packages are missing. Please check README file."
419
+		status-set blocked "IBM MobileFirst server Packages are missing"
420
+		return 
421
+	fi
422
+}
423
+
424
+@when 'mobilefirstserverdb.connected'
425
+@when_not 'mobilefirstserverdb.ready'
426
+function send_sshkeys_mobilefirstserverdbs(){
427
+	SSH_PATH=/root/.ssh
428
+	if [ ! -f  $SSH_PATH/id_rsa.pub ]; then
429
+		juju-log "IBM MFS: Setting up SSH keys."
430
+		ssh-keygen -t rsa -f $SSH_PATH/id_rsa -N ''
431
+	fi
432
+	key="`cat $SSH_PATH/id_rsa.pub`"
433
+	mobilefirstserverdbnames="APPCNTR,WRKLGHT"
434
+	juju-log "DB names is $mobilefirstserverdbnames"
435
+	# To pass the SSH key and DB names to DB2 charm
436
+	relation_call --state=mobilefirstserverdb.connected set_ssh_keys "$key" || true
437
+	relation_call --state=mobilefirstserverdb.connected set_dbs $mobilefirstserverdbnames || true
438
+}
439
+
440
+@when 'mobilefirstserverdb.ready'
441
+@when_not 'ibm-mobilefirst-server.mobilefirstserverdb.configured'
442
+function configure_mobilefirstserverdb(){
443
+
444
+	get_db_values_from_interface
445
+
446
+	if [ -z "$db2_port" ]; then
447
+		juju-log "No data sent yet"
448
+		return 0
449
+	fi
450
+
451
+	#get_db_values_from_interface
452
+
453
+	juju-log "IBM MFS: Got the db2 values : $db2_path $hostname $dbusername $dbuserpw $dbs_created"
454
+	juju-log "IBM MFS: Copying files from remote - Directory: $db2_path Host:$hostname"
455
+	status-set maintenance "Copying mobilefirstserverdb2 jar files from remote DB2 machine"
456
+	scp  -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$hostname:$db2_path/V10.5/java/db2jcc.jar $CHARM_DIR/files || true
457
+	scp  -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$hostname:$db2_path/V10.5/java/db2jcc4.jar $CHARM_DIR/files || true
458
+	scp  -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$hostname:$db2_path/V10.5/java/db2jcc_license_*.jar $CHARM_DIR/files || true
459
+	juju-log "IBM MFS: Copied the files to $CHARM_DIR/files"
460
+	#Updating the passwords for db connection in the required wlp files if its changed and restarting the worklight server. Don't do this if MFS is not installed.
461
+	if [ -d $MobileFirstServer_PATH/WorklightServer ]; then
462
+		#Update the password if changed in server.xml file
463
+		newpassword=`cat $LIBERTY_INSTALL_PATH/usr/servers/worklight/server.xml | grep "WRKLGHT" | grep "WLADMIN" | cut -d"}" -f2 | cut -d" " -f1 | tr -d '"'`
464
+		oldpassword=`cat $LIBERTY_INSTALL_PATH/usr/servers/worklight/server.xml | grep "APPCNTR" | cut -d"}" -f2 | cut -d" " -f1 | tr -d '"'`
465
+		sed -i s/$oldpassword/$newpassword/g $LIBERTY_INSTALL_PATH/usr/servers/worklight/server.xml
466
+		status-set maintenance "Server.xml file updated" 
467
+	fi
468
+	set_state 'ibm-mobilefirst-server.mobilefirstserverdb.configured'
469
+
470
+}
471
+
472
+@when 'mobilefirstserverdb.departed'
473
+function stop_mobilefirstserverdb2(){
474
+	# If relation with db2 is broken as the DBs related to worklight will be deleted and the login credentilas for the consoles will be lost.
475
+	# Without having the login info from the DB,the user cannot login. The user needs to remove the Mobile First server service and deploy it again.
476
+	juju-log "IBM MFS: Removing the jar files when relation is broken"
477
+	rm -rf $CHARM_DIR/files/db2jcc.jar $CHARM_DIR/files/db2jcc4.jar $CHARM_DIR/files/db2jcc_license_*.jar
478
+	juju-log "IBM MFS: JAR files removed"	
479
+	if [ -d $LIBERTY_INSTALL_PATH/usr/servers/worklight ]; then
480
+		server_status=`ps -ef | grep worklight | grep $LIBERTY_INSTALL_PATH/bin/tools/ws-server.jar | awk '{print $(NF-1),$NF}'`
481
+		if [ "$server_status" == "$LIBERTY_INSTALL_PATH/bin/tools/ws-server.jar worklight" ]; then
482
+			juju-log "IBM MFS: stopping worklight server"
483
+			$LIBERTY_INSTALL_PATH/bin/server stop worklight
484
+			if [ $? == 0 ]; then
485
+				juju-log "IBM MFS: worklight server stopped sucessfully"
486
+				remove_state 'ibm-mobilefirst-server.website.started'
487
+				juju-log "Copying the org server.xml file back in website broken fn"
488
+			else
489
+				juju-log "IBM MFS: Failed to stop worklight server"
490
+				return 0
491
+			fi
492
+		fi
493
+		clean_old_installations
494
+		remove_worklightserver
495
+		remove_state 'ibm-mobilefirst-server.installed'
496
+		remove_state 'ibm-mobilefirst-server.mobilefirstserverdb.configured'
497
+	else
498
+		juju-log "IBM MFS: Worklight server does not exist"
499
+	fi
500
+}
501
+
502
+
503
+@when 'website.departed'
504
+function stop_worklight_server(){
505
+	#If relation between wlp and worklight is broken( or when MobileFirst Server service is removed) , uninstalling worklight.
506
+	#Otherwise if we set the relation again or deploy MobileFirst server again, the installation goes wrong with traces of old files present
507
+	juju-log "IBM MFS: In website departed function"
508
+	if [ -d $LIBERTY_INSTALL_PATH/usr/servers/worklight ]; then
509
+		server_status=`ps -ef | grep worklight | grep $LIBERTY_INSTALL_PATH/bin/tools/ws-server.jar | awk '{print $(NF-1),$NF}'`
510
+		if [ "$server_status" == "$LIBERTY_INSTALL_PATH/bin/tools/ws-server.jar worklight" ]; then
511
+			juju-log "IBM MFS: stopping worklight server"
512
+			$LIBERTY_INSTALL_PATH/bin/server stop worklight
513
+			if [ $? == 0 ]; then
514
+				juju-log "IBM MFS: worklight server stopped sucessfully"
515
+				remove_state 'ibm-mobilefirst-server.website.started'		
516
+				juju-log "Copying the org server.xml file back in website broken fn"
517
+			else
518
+				juju-log "IBM MFS: Failed to stop worklight server"
519
+				return 0
520
+			fi
521
+		fi
522
+		clean_old_installations
523
+		remove_worklightserver
524
+		remove_state 'ibm-mobilefirst-server.installed'
525
+		remove_state 'ibm-mobilefirst-server.mobilefirstserverdb.configured'
526
+	else
527
+		juju-log "IBM MFS: Worklight server does not exist"
528
+	fi
529
+
530
+}
531
+
532
+@when_not_all 'config.default.worklight_user' 'config.default.worklight_passwd'
533
+@when_any 'config.changed.worklight_user' 'config.changed.worklight_passwd'
534
+function create_new_userpassword(){
535
+	# Change only if worklight is installed
536
+	if [ -d $LIBERTY_INSTALL_PATH/usr/servers/worklight ]; then
537
+		juju-log "Creating new User/Password"
538
+		status-set active "Updating User Credentials"	
539
+		update_usename_serverxml_file
540
+		status-set active "IBM MFS Updated"
541
+	fi
542
+	
543
+
544
+}
545
+reactive_handler_main
Back to file index

requirements.txt

1
--- 
2
+++ requirements.txt
3
@@ -0,0 +1,2 @@
4
+flake8
5
+pytest
Back to file index

revision

1
--- 
2
+++ revision
3
@@ -0,0 +1 @@
4
+0
Back to file index

tests/00-setup

 1
--- 
 2
+++ tests/00-setup
 3
@@ -0,0 +1,17 @@
 4
+#!/bin/bash
 5
+
 6
+
 7
+DB2_CURL_URL=${DB2_CURL_URL?Error: IBM DB2  curl_url be defined in tests/00-setup}
 8
+DB2_CURL_OPTS=${DB2_CURL_OPTS?Error: IBM DB2  curl_OPTS be defined in tests/00-setup}
 9
+
10
+
11
+# Add a local configuration file
12
+cat << EOF > local.yaml
13
+ibm-repo:
14
+    db2_curl_url: "$DB2_CURL_URL"
15
+    db2_curl_opts: "$DB2_CURL_OPTS"
16
+EOF
17
+
18
+sudo add-apt-repository ppa:juju/stable -y
19
+sudo apt-get update
20
+sudo apt-get install amulet python3 -y
Back to file index

tests/01-deploy.py

 1
--- 
 2
+++ tests/01-deploy.py
 3
@@ -0,0 +1,30 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+import amulet
 7
+import re
 8
+import unittest
 9
+
10
+
11
+class TestDeploy(unittest.TestCase):
12
+    """
13
+    Deployment test for the IBM IM charm.
14
+
15
+    This charm doesn't do much by itself, so we expect functional
16
+    tests to happen in the charms that use this layer (for example, websphere).
17
+    """
18
+    def setUp(self):
19
+        self.d = amulet.Deployment(series='xenial')
20
+        self.d.add('ibm-im', 'cs:~ibmcharmers/xenial/ibm-im')
21
+        self.d.setup(timeout=900)
22
+        self.d.sentry.wait(timeout=1800)
23
+
24
+    def test_deploy_with_placeholder_resource(self):
25
+        # The status message when using placeholder resources will include the
26
+        # string "ibm_im_installer resource". If we see that, the test is
27
+        # successful.
28
+        sentry_re = re.compile('ibm_im_installer resource')
29
+        self.d.sentry.wait_for_messages({"ibm-im": sentry_re})
30
+
31
+
32
+if __name__ == '__main__':
33
+    unittest.main()
Back to file index

tests/01-deploy_mobilefirst.py

 1
--- 
 2
+++ tests/01-deploy_mobilefirst.py
 3
@@ -0,0 +1,89 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+import amulet
 7
+import re
 8
+import unittest
 9
+import os
10
+import yaml
11
+import sys
12
+import requests
13
+
14
+
15
+class TestDeploy(unittest.TestCase):
16
+    """
17
+    Deployment test for the IBM IM charm.
18
+
19
+    This charm doesn't do much by itself, so we expect functional
20
+    tests to happen in the charms that use this layer (for example, websphere).
21
+    """
22
+    def setUp(self):
23
+        self.d = amulet.Deployment(series='trusty')
24
+        local_path = os.path.join(os.path.dirname(__file__), 'local.yaml')
25
+        with open(local_path, "r") as fd:
26
+            config = yaml.safe_load(fd)
27
+
28
+        curl_url = config.get('ibm-repo').get('db2_curl_url')
29
+        if not curl_url:
30
+            message = 'Please provide the curl url from where the ' \
31
+                      'package can be downloaded.'
32
+            amulet.raise_status(amulet.FAIL, msg=message)
33
+            sys.exit(1)
34
+
35
+        curl_opts = config.get('ibm-repo').get('db2_curl_opts')
36
+        if not curl_opts:
37
+            message = 'Please provide the curl_opts to specify the ' \
38
+                      'credentials to download the packages.'
39
+            amulet.raise_status(amulet.FAIL, msg=message)
40
+            sys.exit(1)
41
+
42
+        self.d.add('ibm-db2', 'cs:trusty/ibm-db2')
43
+#        self.d.add('ibm-db2', 'cs:~shilkaul/trusty/ibm-db2-0')
44
+        self.d.add('websphere-liberty',
45
+                   'cs:trusty/websphere-liberty-4')
46
+        self.d.add('ibm-mobilefirst-server',
47
+                   'cs:~ibmcharmers/trusty/ibm-mobilefirst-server')
48
+#        self.d.add('ibm-mobilefirst-server',
49
+#                   'cs:~shilkaul/trusty/ibm-mobilefirst-server-0')
50
+        self.d.configure('websphere-liberty',
51
+                         {'accept-ibm-websphere-license': True})
52
+        self.d.configure('websphere-liberty',
53
+                         {'accept-ibm-java-license': True})
54
+        self.d.configure('websphere-liberty',
55
+                         {'ibm-liberty-version': "8.5.5.8"})
56
+        self.d.configure('websphere-liberty',
57
+                         {'sha_wlp': "a0497da259cf6ad972500ceedbb1227c0ac72de \
58
+                          4adc50a0fa6edc60ea044ab3a32d4f390533c98dee72ad1f36f \
59
+                          a54d09591bb0d0ec6e6245b602e091bf7ea3de"})
60
+
61
+        self.d.configure('ibm-db2', {'license_accepted': True,
62
+                         'curl_url': curl_url, 'curl_opts': curl_opts})
63
+
64
+        self.d.relate('websphere-liberty:website',
65
+                      'ibm-mobilefirst-server:website')
66
+        self.d.relate('ibm-db2:db',
67
+                      'ibm-mobilefirst-server:mobilefirstserverdb')
68
+
69
+        self.d.setup(timeout=1200)
70
+        self.d.sentry.wait(timeout=1200)
71
+
72
+    def test_deploy_with_placeholder_resource(self):
73
+        # The status message when using placeholder resources will include the
74
+        # string "ibm_mobilefirstserver_installer resource". If we see that,
75
+        # the test is successful.
76
+        sentry_re = re.compile('ibm_mobilefirstserver_installer resource')
77
+        self.d.sentry.wait_for_messages({"ibm-mobilefirstserver": sentry_re})
78
+
79
+    def test_mobilefirst_server_deployed(self):
80
+        self.assertTrue(self.d.deployed)
81
+        unit = self.d.sentry['ibm-mobilefirst-server'][0]
82
+        url = 'http://%s:9081/appcenterconsole' % unit.info['public-address']
83
+        url1 = 'https://%s:9443/appcenterconsole' % unit.info['public-address']
84
+        print('Url is %s' % url)
85
+        response = requests.get(url, verify=False)
86
+        response = requests.get(url1, verify=False)
87
+        # Raise an exception if the url was not a valid web page.
88
+        response.raise_for_status()
89
+
90
+
91
+if __name__ == '__main__':
92
+    unittest.main()
Back to file index

tests/bundles.yaml

 1
--- 
 2
+++ tests/bundles.yaml
 3
@@ -0,0 +1,10 @@
 4
+ibm-db2-bundle: 
 5
+  services: 
 6
+    "ibm-db2": 
 7
+      charm: "ibm-db2"
 8
+      num_units: 1
 9
+      annotations: 
10
+        "gui-x": "300"
11
+        "gui-y": "300"
12
+  relations: []
13
+  series: trusty
Back to file index

tests/local.yaml

1
--- 
2
+++ tests/local.yaml
3
@@ -0,0 +1,3 @@
4
+ibm-repo:
5
+    db2_curl_url: "http://9.124.100.99/debs/ibm-db2/DB2_Svr_10.5.0.3_Linux_x86-64.tar.gz?sha512=ae20be99e3cd2cef24d53a28331871d7193cfc7f7c24c580e6e78dc58c9ffb364cbcc69cb3773d2f0135b8c9be7ee40a7dc4e09fec0a4d731c4864bbba87d31e"
6
+    db2_curl_opts: "-u root:root123"
Back to file index

tests/tests.yaml

1
--- 
2
+++ tests/tests.yaml
3
@@ -0,0 +1,4 @@
4
+packages:
5
+  - amulet
6
+  - python3
7
+  - unzip
Back to file index

tox.ini

 1
--- 
 2
+++ tox.ini
 3
@@ -0,0 +1,12 @@
 4
+[tox]
 5
+skipsdist=True
 6
+envlist = py34, py35
 7
+skip_missing_interpreters = True
 8
+
 9
+[testenv]
10
+commands = py.test -v
11
+deps =
12
+    -r{toxinidir}/requirements.txt
13
+
14
+[flake8]
15
+exclude=docs