← Back to team overview

cf-charmers team mailing list archive

Re: [Merge] lp:~johnsca/charms/trusty/cloudfoundry/trunk-webadmin into lp:~cf-charmers/charms/trusty/cloudfoundry/trunk

 

Review: Approve

Thanks for the additional changes. LGTM

There are some inline rambles but nothing that has to be changed now.

Diff comments:

> === modified file 'charmhelpers/core/services/base.py'
> --- charmhelpers/core/services/base.py	2014-09-18 19:34:19 +0000
> +++ charmhelpers/core/services/base.py	2014-10-31 16:12:00 +0000
> @@ -1,6 +1,7 @@
>  import os
> -import re
>  import json
> +import hashlib
> +import inspect
>  from collections import Iterable
>  
>  from charmhelpers.core import host
> @@ -105,6 +106,7 @@
>              manager.manage()
>          """
>          self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json')
> +        self._data_hash_file = os.path.join(hookenv.charm_dir(), 'DATA-HASHES.json')
>          self._ready = None
>          self.services = {}
>          for service in services or []:
> @@ -131,13 +133,16 @@
>              raise
>  
>      def provide_data(self):
> -        hook_name = hookenv.hook_name()
> -        for service in self.services.values():
> +        for service_name, service in self.services.items():
>              for provider in service.get('provided_data', []):
> -                if re.match(r'{}-relation-(joined|changed)'.format(provider.name), hook_name):
> -                    data = provider.provide_data()
> +                for rel_id in hookenv.relation_ids(provider.name):
> +                    pargs = inspect.getargspec(provider.provide_data).args

We do need to document this change. A card for that would be nice.

> +                    if len(pargs) > 1:
> +                        data = provider.provide_data(self, service_name, rel_id)
> +                    else:
> +                        data = provider.provide_data()
>                      if provider._is_ready(data):
> -                        hookenv.relation_set(None, data)
> +                        hookenv.relation_set(rel_id, data)
>  
>      def reconfigure_services(self, *service_names):
>          """
> @@ -148,10 +153,17 @@
>          """
>          for service_name in service_names or self.services.keys():
>              if self.is_ready(service_name):
> -                self.fire_event('data_ready', service_name)
> -                self.fire_event('start', service_name, default=[
> -                    service_restart,
> -                    manage_ports])
> +                was_ready = self.was_ready(service_name)
> +                data_changed = self.data_changed(service_name)
> +                if data_changed or not was_ready:
> +                    hookenv.log('Restarting service: %s' % service_name)
> +                    # only do work (restart) if the data says we're in a new state
> +                    self.fire_event('data_ready', service_name)
> +                    self.fire_event('start', service_name, default=[
> +                        service_restart,
> +                        manage_ports])
> +                else:
> +                    hookenv.log('Skipping service restart: %s' % service_name)
>                  self.save_ready(service_name)
>              else:
>                  if self.was_ready(service_name):
> @@ -246,6 +258,30 @@
>          self._load_ready_file()
>          return service_name in self._ready
>  
> +    def merged_data(self, service_name):
> +        service = self.get_service(service_name)
> +        context = {}
> +        for ctx in service.get('required_data', []):
> +            context.update(ctx)
> +        return context
> +
> +    def _get_data_hashes(self, service_name):
> +        data = self.merged_data(service_name)
> +        hashes = {}
> +        if os.path.exists(self._data_hash_file):
> +            with open(self._data_hash_file) as fp:
> +                hashes = json.load(fp)
> +        old_hash = hashes.get(service_name)
> +        new_hash = hashlib.md5(json.dumps(data, sort_keys=True)).hexdigest()
> +        hashes[service_name] = new_hash
> +        with open(self._data_hash_file, 'w') as fp:
> +            json.dump(hashes, fp)

we do leave a lot of these files around now. In the end it might make sense to revisit when/how we dump caches to the fs. even if its a centralized directory and naming convention, all the way up to an embedded db/kv store to using an etcd for scratch space.

> +        return old_hash, new_hash
> +
> +    def data_changed(self, service_name):
> +        old_hash, new_hash = self._get_data_hashes(service_name)
> +        return old_hash != new_hash
> +
>      def update_status_working(self):
>          if hookenv.juju_status()['status'] == 'blocked':
>              hookenv.juju_status('churning')
> 
> === modified file 'charmhelpers/core/services/helpers.py'
> --- charmhelpers/core/services/helpers.py	2014-08-21 17:34:34 +0000
> +++ charmhelpers/core/services/helpers.py	2014-10-31 16:12:00 +0000
> @@ -113,10 +113,7 @@
>          self.perms = perms
>  
>      def __call__(self, manager, service_name, event_name):
> -        service = manager.get_service(service_name)
> -        context = {}
> -        for ctx in service.get('required_data', []):
> -            context.update(ctx)
> +        context = manager.merged_data(service_name)
>          templating.render(self.source, self.target, context,
>                            self.owner, self.group, self.perms)
>  
> 
> === modified file 'cloudfoundry/contexts.py'
> --- cloudfoundry/contexts.py	2014-10-27 19:48:56 +0000
> +++ cloudfoundry/contexts.py	2014-10-31 16:12:00 +0000
> @@ -31,6 +31,13 @@
>                                                 rid=rid, unit=unit))
>          return list(units)
>  
> +    def get_first(self, key=None):
> +        data = self[self.name][0]
> +        return data[key] if key is not None else data
> +
> +    def get_all(self, key):
> +        return [u[key] for u in self[self.name]]
> +
>  
>  class StoredContext(dict):
>      """
> @@ -100,7 +107,7 @@
>      def get_data(self):
>          RelationContext.get_data(self)
>          if self.is_ready():
> -            for unit in self['db']:
> +            for unit in self[self.name]:
>                  if 'port' not in unit:
>                      unit['port'] = '3306'
>                  unit['dsn'] = self.dsn_template.format(**unit)
> @@ -114,7 +121,7 @@
>  
>  class UAARelation(RelationContext):
>      name = 'uaa'
> -    interface = 'http'
> +    interface = 'uaa'
>      required_keys = ['login_client_secret', 'admin_client_secret', 'cc_client_secret', 'cc_token_secret',
>                       'service_broker_client_secret', 'servicesmgmt_client_secret', 'port']
>      port = 8081
> @@ -168,6 +175,18 @@
>          }
>  
>  
> +class UAADBRelation(MysqlRelation):
> +    name = 'uaadb'
> +
> +    def provide_data(self, manager, service_name, rel_id):
> +        data = {}
> +        if manager.is_ready(service_name):
> +            # proxy data from the MysqlRelation to this relation

Thanks, the comment helps here.

> +            mysql = MysqlRelation()
> +            data = mysql.get_first()
> +        return data
> +
> +
>  class LoginRelation(RelationContext):
>      name = 'login'
>      interface = 'http'
> @@ -362,17 +381,16 @@
>          }
>  
>  
> -class CloudControllerDBRelation(RelationContext):
> -    name = 'cc-db'
> -    interface = 'controller-db'
> -    required_keys = MysqlRelation.required_keys
> +class CloudControllerDBRelation(MysqlRelation):
> +    name = 'ccdb'
>  
> -    @classmethod
> -    def send_data(cls, job_name):
> -        # using send_data instead of provide_data to delay it until data_ready
> -        data = MysqlRelation()['db'][0]
> -        for rid in hookenv.relation_ids(cls.name):
> -            hookenv.relation_set(rid, data)
> +    def provide_data(self, manager, service_name, rel_id):
> +        data = {}
> +        if manager.is_ready(service_name):
> +            # proxy data from the MysqlRelation to this relation
> +            mysql = MysqlRelation()
> +            data = mysql.get_first()
> +        return data
>  
>  
>  class RouterRelation(RelationContext):
> 
> === modified file 'cloudfoundry/mapper.py'
> --- cloudfoundry/mapper.py	2014-07-31 22:05:01 +0000
> +++ cloudfoundry/mapper.py	2014-10-31 16:12:00 +0000
> @@ -61,7 +61,7 @@
>  
>      #@@ This may need to be adjusted to scale / HA the databases.
>      """
> -    db = data.get('db', data.get('cc-db'))[0]
> +    db = data.get('db', data.get('ccdb'))[0]
>  
>      job_db = dict(tag='cc',
>                    name=db['database'])
> 
> === modified file 'cloudfoundry/services.py'
> --- cloudfoundry/services.py	2014-09-25 15:23:46 +0000
> +++ cloudfoundry/services.py	2014-10-31 16:12:00 +0000
> @@ -12,7 +12,7 @@
>          'description': '',
>          'jobs': [{
>              'job_name': 'cloud_controller_clock',
> -            'mapping': {'cc-db': mapper.ccdb},
> +            'mapping': {'ccdb': mapper.ccdb},
>              'provided_data': [],
>              'required_data': [contexts.NatsRelation,
>                                contexts.LTCRelation,
> @@ -30,7 +30,7 @@
>          'description': '',
>          'jobs': [
>              {'job_name': 'cloud_controller_clock',
> -             'mapping': {'cc-db': mapper.ccdb},
> +             'mapping': {'ccdb': mapper.ccdb},
>               'provided_data': [],
>               'required_data': [contexts.NatsRelation,
>                                 contexts.LTCRelation,
> @@ -62,9 +62,6 @@
>                                contexts.UAARelation,
>                                contexts.CloudControllerRelation.remote_view,
>                                ],
> -            'data_ready': [
> -                contexts.CloudControllerDBRelation.send_data,
> -            ],
>          }]
>      },
>  
> @@ -82,9 +79,7 @@
>                                 contexts.UAARelation,
>                                 contexts.CloudControllerRelation.remote_view,
>                                 ],
> -             'data_ready': [
> -                 contexts.CloudControllerDBRelation.send_data,
> -             ]},
> +             },
>              {'job_name': 'metron_agent',
>               'required_data': [contexts.LTCRelation,
>                                 contexts.NatsRelation,
> @@ -98,7 +93,7 @@
>          'description': '',
>          'jobs': [
>              {'job_name': 'cloud_controller_worker',
> -             'mapping': {'cc-db': mapper.ccdb},
> +             'mapping': {'ccdb': mapper.ccdb},
>               'provided_data': [],
>               'required_data': [contexts.NatsRelation,
>                                 contexts.LTCRelation,
> @@ -116,7 +111,7 @@
>          'description': '',
>          'jobs': [
>              {'job_name': 'cloud_controller_worker',
> -             'mapping': {'cc-db': mapper.ccdb},
> +             'mapping': {'ccdb': mapper.ccdb},
>               'provided_data': [],
>               'required_data': [contexts.NatsRelation,
>                                 contexts.LTCRelation,
> @@ -271,11 +266,11 @@
>          'jobs': [
>              {'job_name': 'uaa',
>               'mapping': {'db': mapper.uaadb},
> -             'provided_data': [contexts.UAARelation],
> +             'provided_data': [contexts.UAARelation,
> +                               contexts.UAADBRelation],
>               'required_data': [contexts.MysqlRelation,
>                                 contexts.NatsRelation,
> -                               contexts.UAARelation.remote_view]
> -             }
> +                               contexts.UAARelation.remote_view]},
>          ]
>      },
>  
> 
> === modified file 'cloudfoundry/utils.py'
> --- cloudfoundry/utils.py	2014-10-27 19:48:56 +0000
> +++ cloudfoundry/utils.py	2014-10-31 16:12:00 +0000
> @@ -10,7 +10,6 @@
>  from contextlib import contextmanager
>  from functools import partial, wraps
>  
> -import jujuclient
>  import requests
>  import yaml
>  
> @@ -232,6 +231,7 @@
>  
>  
>  def get_client():
> +    import jujuclient
>      global _client
>      if _client is None:
>          def env_connection():
> 
> === modified file 'tests/test_jobs.py'
> --- tests/test_jobs.py	2014-09-29 22:12:51 +0000
> +++ tests/test_jobs.py	2014-10-31 16:12:00 +0000
> @@ -73,8 +73,6 @@
>          self.assertIsInstance(services[0]['data_ready'][4],
>                                tasks.JobTemplates)
>          services = jobs.build_service_block('cloud-controller-v1')
> -        # Show that we include both default and additional handlers
> +        # Show that we include default handlers
>          self.assertIsInstance(services[0]['data_ready'][4],
>                                tasks.JobTemplates)
> -        self.assertEqual(services[0]['data_ready'][-1],
> -                         contexts.CloudControllerDBRelation.send_data)
> 


-- 
https://code.launchpad.net/~johnsca/charms/trusty/cloudfoundry/trunk-webadmin/+merge/240033
Your team Cloud Foundry Charmers is subscribed to branch lp:~cf-charmers/charms/trusty/cloudfoundry/trunk.


Follow ups

References