cf-charmers team mailing list archive
-
cf-charmers team
-
Mailing list archive
-
Message #00586
[Merge] lp:~johnsca/charms/trusty/cloudfoundry/adminpass into lp:~cf-charmers/charms/trusty/cloudfoundry/trunk
Cory Johns has proposed merging lp:~johnsca/charms/trusty/cloudfoundry/adminpass into lp:~cf-charmers/charms/trusty/cloudfoundry/trunk.
Requested reviews:
Cloud Foundry Charmers (cf-charmers)
For more details, see:
https://code.launchpad.net/~johnsca/charms/trusty/cloudfoundry/adminpass/+merge/240606
Dynamically generate password for default CF admin user, with config option to override.
Also, correctly populate the request-uri value in the uaa.clients.servicesmgmt block, although the package isn't available yet.
--
https://code.launchpad.net/~johnsca/charms/trusty/cloudfoundry/adminpass/+merge/240606
Your team Cloud Foundry Charmers is requested to review the proposed merge of lp:~johnsca/charms/trusty/cloudfoundry/adminpass into lp:~cf-charmers/charms/trusty/cloudfoundry/trunk.
=== modified file 'README.rst'
--- README.rst 2014-10-16 15:01:08 +0000
+++ README.rst 2014-11-04 16:50:28 +0000
@@ -61,6 +61,19 @@
cf push
+Default Admin User
+------------------
+
+By default, an admin user is created with the username of "admin" and a
+randomly generated password. The password can be retrived using the helper:
+
+ . helpers.sh
+ cfadminpass
+
+You can use the `admin_password` config option to override the generated
+password.
+
+
Development
-----------
=== modified file 'cloudfoundry/contexts.py'
--- cloudfoundry/contexts.py 2014-10-31 16:11:51 +0000
+++ cloudfoundry/contexts.py 2014-11-04 16:50:28 +0000
@@ -32,6 +32,8 @@
return list(units)
def get_first(self, key=None):
+ if not self.get(self.name, []):
+ return None if key is not None else {}
data = self[self.name][0]
return data[key] if key is not None else data
@@ -144,6 +146,20 @@
def erb_mapping(self):
data = self[self.name][0]
+ creds = CloudFoundryCredentials()
+ users = []
+ if creds.is_ready():
+ users.append(
+ '%s|%s|scim.write,scim.read,openid,cloud_controller.admin' % (
+ creds.get_first('admin-user'),
+ creds.get_first('admin-password')
+ )
+ )
+ orch = OrchestratorRelation()
+ sru = None
+ if orch.is_ready():
+ sru = 'http://servicesmgmt.{}/auth/cloudfoundry/callback'.format(
+ orch.get_first('domain'))
return {
'uaa.login.client_secret': data['login_client_secret'],
'uaa.admin.client_secret': data['admin_client_secret'],
@@ -152,9 +168,7 @@
'uaa.port': data['port'],
'uaa.require_https': False, # FIXME: Add SSL as an option; requires cert
'uaa.no_ssl': True,
- 'uaa.scim.users': [
- 'admin|admin|scim.write,scim.read,openid,cloud_controller.admin', # FIXME: Don't hard-code
- ],
+ 'uaa.scim.users': users,
'uaa.clients': {
'cc_service_broker_client': {
'secret': data['service_broker_client_secret'],
@@ -167,7 +181,7 @@
'authorized-grant-types': 'authorization_code,client_credentials,password,implicit',
'autoapprove': True,
'override': True,
- 'redirect-uri': 'http://servicesmgmt.10.244.0.34.xip.io/auth/cloudfoundry/callback',
+ 'redirect-uri': sru,
'scope': 'openid,cloud_controller.read,cloud_controller.write',
'secret': data['servicesmgmt_client_secret'],
},
@@ -422,13 +436,22 @@
"""
name = "credentials"
interface = "cloudfoundry-credentials"
+ required_keys = ['admin-user', 'admin-password']
+
+ def get_admin_password(self):
+ config = hookenv.config()
+ if config['admin_password']:
+ return config['admin_password']
+ else:
+ secret_context = StoredContext('cf-secrets.yml', {
+ 'admin_password': host.pwgen(20),
+ })
+ return secret_context['admin_password']
def provide_data(self):
- # TODO: this must come from generated or a UAA related identity
- # provider
return {
'admin-user': 'admin',
- 'admin-password': 'admin'
+ 'admin-password': self.get_admin_password(),
}
=== modified file 'cloudfoundry/services.py'
--- cloudfoundry/services.py 2014-10-31 15:34:27 +0000
+++ cloudfoundry/services.py 2014-11-04 16:50:28 +0000
@@ -270,6 +270,7 @@
contexts.UAADBRelation],
'required_data': [contexts.MysqlRelation,
contexts.NatsRelation,
+ contexts.CloudFoundryCredentials,
contexts.UAARelation.remote_view]},
]
},
=== modified file 'config.yaml'
--- config.yaml 2014-09-30 21:15:05 +0000
+++ config.yaml 2014-11-04 16:50:28 +0000
@@ -1,4 +1,11 @@
options:
+ admin_password:
+ type: string
+ description: >
+ The password for the default admin user in Cloud Foundry.
+ If left blank, a random password will be generated, which can be
+ retrieved using the cfadminpass helper from helpers.sh.
+ default: ""
admin_secret:
type: string
description: >
=== modified file 'helpers.sh'
--- helpers.sh 2014-10-06 06:48:12 +0000
+++ helpers.sh 2014-11-04 16:50:28 +0000
@@ -1,9 +1,14 @@
+function cfadminpass() {
+ juju run --unit uaa/0 'relation-get -r $(relation-ids credentials) admin-password cloudfoundry/0'
+}
+
+
function cflogin() {
ENDPOINT=`juju status haproxy/0 |grep public-address|cut -f 2 -d : `
IP=`dig +short $ENDPOINT`
# get the _IP_ of the public address
cf api http://api.${IP}.xip.io
- cf auth admin admin
+ cf auth admin "$(cfadminpass)"
cf create-space -o juju-org my-space
cf target -o juju-org -s my-space
}
=== modified file 'hooks/common.py'
--- hooks/common.py 2014-10-14 07:05:52 +0000
+++ hooks/common.py 2014-11-04 16:50:28 +0000
@@ -109,12 +109,20 @@
charm = deployment.get_charm_for(service_name)
if 'orchestrator' in charm.metadata.get('requires', {}):
try:
- env.add_relation(orchestrator, service_name)
+ env.add_relation('{}:orchestrator'.format(orchestrator), service_name)
except EnvError as e:
if e.message.endswith('relation already exists'):
- continue # existing relations are ok, just skip
+ pass # existing relations are ok, just skip
else:
hookenv.log('Error adding orchestrator relation: {}'.format(str(e)), hookenv.ERROR)
+ if 'credentials' in charm.metadata.get('requires', {}):
+ try:
+ env.add_relation('{}:credentials'.format(orchestrator), service_name)
+ except EnvError as e:
+ if e.message.endswith('relation already exists'):
+ pass # existing relations are ok, just skip
+ else:
+ hookenv.log('Error adding credentials relation: {}'.format(str(e)), hookenv.ERROR)
env.expose('haproxy')
finally:
env.close()
=== modified file 'tests/test_contexts.py'
--- tests/test_contexts.py 2014-10-14 07:05:52 +0000
+++ tests/test_contexts.py 2014-11-04 16:50:28 +0000
@@ -200,11 +200,31 @@
class TestCloudFoundryCredentials(unittest.TestCase):
@mock.patch('charmhelpers.core.hookenv.charm_dir', lambda: 'charm_dir')
@mock.patch('charmhelpers.core.services.RelationContext.get_data', mock.Mock())
- def test_provide_data(self):
- result = contexts.CloudFoundryCredentials().provide_data()
- self.assertEqual(result, {
- "admin-password": "admin",
- "admin-user": "admin"
+ @mock.patch('charmhelpers.core.hookenv.config')
+ def test_provide_data_config(self, config):
+ config.return_value = {'admin_password': 'test'}
+ result = contexts.CloudFoundryCredentials().provide_data()
+ self.assertEqual(result, {
+ "admin-password": "test",
+ "admin-user": "admin"
+ })
+
+ @mock.patch('charmhelpers.core.hookenv.charm_dir', lambda: 'charm_dir')
+ @mock.patch('charmhelpers.core.services.RelationContext.get_data', mock.Mock())
+ @mock.patch('cloudfoundry.contexts.StoredContext')
+ @mock.patch('charmhelpers.core.host.pwgen')
+ @mock.patch('charmhelpers.core.hookenv.config')
+ def test_provide_data_gen(self, config, pwgen, StoredContext):
+ config.return_value = {'admin_password': ''}
+ pwgen.return_value = 'pw1'
+ StoredContext.return_value = {'admin_password': 'pw2'}
+ result = contexts.CloudFoundryCredentials().provide_data()
+ self.assertEqual(result, {
+ "admin-password": "pw2",
+ "admin-user": "admin"
+ })
+ StoredContext.assert_called_once_with('cf-secrets.yml', {
+ 'admin_password': 'pw1',
})
Follow ups