def test_named_volumes(self):
     """Make sure that named volumes are correctly registered"""
     volume_name = "control_unittest_volume{}".format(
         random.randint(1, 65535))
     self.container_volumes.append(volume_name)
     self.image = 'busybox'
     self.conf = {
         "image": self.image,
         "container": {
             "name": self.container_name,
             "hostname": "busybox",
             "volumes": ["{}:/var".format(volume_name)]
         }
     }
     serv = create_service(self.conf, './Controlfile')
     container = Container(serv).create(prod=False)
     container.start()
     self.assertEqual(len(container.inspect['Mounts']), 1)
     self.assertEqual(len(container.inspect['Mounts'][0]['Name']),
                      len(volume_name))
     self.assertEqual(container.inspect['Mounts'][0]['Destination'], '/var')
     self.assertTrue(container.inspect['Mounts'][0]['Source'].startswith(
         '/var/lib/docker/volumes'),
                     msg="Unexpected mount source: {}".format(
                         container.inspect['Mounts'][0]['Source']))
    def test_anon_volumes(self):
        """
        Make sure that anonymous volumes are correctly registered

        Currently unimplemented and will fail
        """
        self.image = 'busybox'
        self.conf = {
            "image": self.image,
            "container": {
                "name": self.container_name,
                "hostname": "busybox",
                "volumes": ["/var"]
            }
        }
        serv = create_service(self.conf, './Controlfile')
        container = Container(serv).create(prod=False)
        container.start()
        self.assertEqual(len(container.inspect['Mounts']), 1)
        self.assertEqual(len(container.inspect['Mounts'][0]['Name']), 65)
        self.assertEqual(container.inspect['Mounts'][0]['Destination'], '/var')
        self.assertTrue(container.inspect['Mounts'][0]['Source'].startswith(
            '/var/lib/docker/volumes'),
                        msg="Unexpected mount source: {}".format(
                            container.inspect['Mount'][0]['Source']))
Ejemplo n.º 3
0
 def test_unspecified_service(self):
     """
     A Control service does not need to specify a service name if it
     specifies a container name
     """
     service = {"image": "busybox", "container": {"name": "example"}}
     name, ret = normalize_service(
         create_service(deepcopy(service), './Controlfile'), {}, {})
     self.assertEqual(ret['container']['name'], "example")
     self.assertEqual(name, "example")
     self.assertIn('hostname', ret['container'])
     self.assertEqual(ret['container']['hostname'], "example")
Ejemplo n.º 4
0
 def test_name_suffixes(self):
     """check for suffix changes"""
     service = {
         "service": "example",
         "image": "busybox",
         "container": {
             "name": "example",
             "hostname": "service"
         }
     }
     options = {"name": {"suffix": ".company"}}
     name, ret = normalize_service(
         create_service(deepcopy(service), './Controlfile'), options, {})
     self.assertEqual(ret['container']['name'], "example.company")
     self.assertEqual(name, "example")
     self.assertEqual(ret['container']['hostname'], "service")
     del service['service']
     name, ret = normalize_service(
         create_service(deepcopy(service), './Controlfile'), options, {})
     self.assertEqual(ret['container']['name'], "example.company")
     self.assertEqual(name, "example",
                      "service name is affected by name suffix")
     self.assertEqual(ret['container']['hostname'], "service")
Ejemplo n.º 5
0
    def create_service(self, data, service_name, options, variables, ctrlfile):
        """
        Determine if data is a Metaservice or Uniservice

        Variables only exist to be applied if they have been defined up the
        chain of discovered Controlfiles. You don't get to randomly define a
        variable somewhere in a web of included Controlfiles and have that
        apply everywhere.
        """
        self.logger.debug('Received %i variables', len(variables))
        while 'controlfile' in data:
            ctrlfile = data['controlfile']
            # TODO write a test that gets a FileNotFound thrown from here
            data = self.read_in_file(ctrlfile)
        data['service'] = service_name

        services_in_data = 'services' in data
        services_is_list = isinstance(data.get('services', None), list)
        if services_in_data:
            self.logger.debug('metaservice named %s', service_name)
            self.logger.debug('services_is_list %s', services_is_list)
            self.logger.debug(data)
        # Recursive step
        if services_in_data and not services_is_list:
            self.logger.debug('found Metaservice %s', data['service'])
            metaservice = MetaService(data, ctrlfile)
            opers = satisfy_nested_options(outer=options,
                                           inner=data.get('options', {}))
            nvars = copy.deepcopy(variables)
            nvars.update(_substitute_vars(data.get('vars', {}), variables))
            nvars.update(os.environ)
            for name, serv in data['services'].items():
                metaservice.services += self.create_service(
                    serv, name, opers, nvars, ctrlfile)
            self.push_service_into_list(metaservice.service, metaservice)
            return metaservice.services
        # No more recursing, we have concrete services now
        try:
            serv = create_service(data, ctrlfile)
        except InvalidControlfile as e:
            self.logger.warning(e)
            return []
        variables['SERVICE'] = serv.service
        if isinstance(serv, ImageService):
            name, service = normalize_service(serv, options, variables)
            self.push_service_into_list(name, service)
            return [name]
        self.push_service_into_list(serv.service, serv)
        return [serv.service]
Ejemplo n.º 6
0
 def test_happy_path(self):
     """Make sure that the defaults still work"""
     self.conf = {
         "image": 'busybox',
         "container": {
             "name": self.container_name,
             "hostname": "happy_path"
         }
     }
     serv = create_service(deepcopy(self.conf), './Controlfile')
     container = Container(serv)
     self.assertEqual(container.service.expected_timeout, 10)
     self.assertEqual(container.service['name'], self.container_name)
     self.assertEqual(container.service['hostname'], self.conf['container']['hostname'])
     self.assertEqual(container.service.image, self.conf['image'])
Ejemplo n.º 7
0
 def test_dns_search(self):
     """test that dns search makes it into the host config"""
     self.image = 'busybox'
     self.conf = {
         "image": self.image,
         "container": {
             "name": self.container_name,
             "dns_search": [
                 "example"
             ]
         }
     }
     serv = create_service(deepcopy(self.conf), './Controlfile')
     conf_copy = serv.prepare_container_options(prod=False)
     self.assertEqual(
         conf_copy['host_config']['DnsSearch'][0],
         self.conf["container"]["dns_search"][0])
Ejemplo n.º 8
0
 def test_expected_timeout(self):
     """Test mirroring unspecified values and overriding default timeout"""
     self.conf = {
         "image": 'busybox',
         "expected_timeout": 3,
         "container": {
             "name": self.container_name
         }
     }
     serv = create_service(deepcopy(self.conf), './Controlfile')
     container = Container(serv)
     self.assertEqual(container.service.expected_timeout, 3)
     self.assertEqual(container.service['name'], self.container_name)
     self.assertEqual(
         container.service['hostname'],
         self.container_name,
         msg='Unspecified hostname not being mirrored from container name')
     self.assertEqual(container.service.image, self.conf['image'])
Ejemplo n.º 9
0
 def test_env_var_parsing(self):
     """
     Need to test that environment variables are always getting parsed
     correctly
     """
     self.image = 'busybox'
     self.conf = {
         "image": self.image,
         "container": {
             "name": self.container_name,
             "hostname": "grafana",
             "environment": [
                 "PASSWORD=password",
             ]
         }
     }
     serv = create_service(deepcopy(self.conf), './Controlfile')
     conf_copy = serv.prepare_container_options(prod=False)
     self.assertEqual(conf_copy['environment'][0], self.conf['container']['environment'][0])
Ejemplo n.º 10
0
 def test_value_substitution(self):
     """Test name substitution working"""
     self.image = 'busybox'
     self.conf = {
         "image": self.image,
         "container": {
             "name": "{container}.{{{{COLLECTIVE}}}}".format(container=self.container_name),
             "environment": [
                 "DOMAIN={{COLLECTIVE}}.petrode.com"
             ],
             "volumes": [
                 "/mnt/log/{{COLLECTIVE}}:/var/log"
             ],
             "dns_search": [
                 "petrode",
                 "{{COLLECTIVE}}.petrode"
             ]
         }
     }
     os.environ['COLLECTIVE'] = 'example'
     serv = create_service(self.conf, './Controlfile')
     conf_copy = serv.prepare_container_options(prod=False)
     self.assertEqual(
         conf_copy['name'],
         '{container}.{{{{COLLECTIVE}}}}'.format(container=self.container_name))
     self.assertEqual(
         conf_copy['environment'][0],
         'DOMAIN={{COLLECTIVE}}.petrode.com')
     self.assertEqual(
         conf_copy['host_config']['Binds'][0],
         "/mnt/log/{{COLLECTIVE}}:/var/log")
     self.assertEqual(
         conf_copy['volumes'][0],
         "/var/log")
     self.assertEqual(
         conf_copy['host_config']['DnsSearch'][0],
         "petrode")
     self.assertEqual(
         conf_copy['host_config']['DnsSearch'][1],
         "{{COLLECTIVE}}.petrode")
     del os.environ['COLLECTIVE']
Ejemplo n.º 11
0
 def test_volume_parsing(self):
     """Make sure that volumes get created correctly"""
     self.image = 'busybox'
     self.conf = {
         "image": self.image,
         "container": {
             "name": "grafana",
             "hostname": "grafana",
             "volumes": [
                 "/var",
                 "named-user:/usr",
                 "/mnt/usrbin:/usr/bin",
             ]
         }
     }
     serv = create_service(deepcopy(self.conf), './Controlfile')
     conf_copy = serv.prepare_container_options(prod=False)
     self.assertEqual(conf_copy['host_config']['Binds'][0], self.conf['container']['volumes'][1])
     self.assertEqual(conf_copy['host_config']['Binds'][1], self.conf['container']['volumes'][2])
     self.assertEqual(conf_copy['volumes'][0], '/var')
     self.assertEqual(conf_copy['volumes'][1], '/usr')
     self.assertEqual(conf_copy['volumes'][2], '/usr/bin')
    def test_mounted_volumes(self):
        """Make sure that mounted volumes are correctly registered"""
        import tempfile

        temp_dir = tempfile.TemporaryDirectory()
        self.image = 'busybox'
        self.conf = {
            "image": self.image,
            "container": {
                "name": self.container_name,
                "hostname": "busybox",
                "volumes": ["{}:/var".format(temp_dir.name)]
            }
        }
        serv = create_service(self.conf, './Controlfile')
        container = Container(serv).create(prod=False)
        container.start()
        self.assertEqual(len(container.inspect['Mounts']), 1)
        self.assertEqual(container.inspect['Mounts'][0]['Destination'], '/var')
        self.assertTrue(
            container.inspect['Mounts'][0]['Source'] == temp_dir.name,
            msg="Unexpected mount source: {}".format(
                container.inspect['Mounts'][0]['Source']))
        temp_dir.cleanup()
Ejemplo n.º 13
0
    def __init__(self, controlfile_location, force_user=False):
        """
        There's two types of Controlfiles. A multi-service file that
        allows some meta-operations on top of the other kind of
        Controlfile. The second kind is the single service Controlfile.
        Full Controlfiles can reference both kinds files to load in more
        options for meta-services.
        """
        self.logger = logging.getLogger('control.controlfile.Controlfile')
        self.services = {
            "required":
            MetaService(
                {
                    'service': 'required',
                    'required': True,
                    'services': []
                }, controlfile_location),
            "optional":
            MetaService(
                {
                    'service': 'optional',
                    'required': False,
                    'services': []
                }, controlfile_location)
        }
        variables = {
            "CONTROL_DIR": dn(dn(dn(os.path.abspath(__file__)))),
            "CONTROL_PATH": dn(dn(os.path.abspath(__file__))),
            "CONTROL_SESSION_UUID": uuid.uuid4(),
            "UID": os.getuid(),
            "GID": os.getgid(),
            "HOSTNAME": socket.gethostname(),
        }
        git = {}
        with subprocess.Popen(['git', 'rev-parse', '--show-toplevel'],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE) as p:
            p.wait()
            if p.returncode == 0:
                root_dir, _ = p.communicate()
                root_dir = root_dir.decode('utf-8').strip()
                git['GIT_ROOT_DIR'] = root_dir
                with subprocess.Popen(
                    ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE) as q:
                    q.wait()
                    if q.returncode == 0:
                        branch, _ = q.communicate()
                        git['GIT_BRANCH'] = branch.decode('utf-8').strip()
                with subprocess.Popen(['git', 'rev-parse', 'HEAD'],
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.PIPE) as q:
                    q.wait()
                    if q.returncode == 0:
                        commit, _ = q.communicate()
                        git['GIT_COMMIT'] = commit.decode('utf-8').strip()
                        git['GIT_SHORT_COMMIT'] = git['GIT_COMMIT'][:7]
        variables.update(git)
        variables.update(os.environ)

        data = self.read_in_file(controlfile_location)
        if not data:
            raise InvalidControlfile(controlfile_location, "empty Controlfile")
        # Check if this is a single service Controlfile, if it is, wrap in a
        # metaservice.
        if 'services' not in data:
            # Temporarily create a service to take advantage of service name guessing
            serv = create_service(copy.deepcopy(data), controlfile_location)
            data = {"services": {serv.service: data}}
        # Check if we are running --as-me, if we are, make sure that we start
        # the process with the user's UID and GID
        if force_user:
            if 'options' not in data:
                data['options'] = {}
            if 'user' not in data['options']:
                data['options']['user'] = {}
            data['options']['user']['replace'] = "{UID}:{GID}"

        self.logger.debug("variables to substitute in: %s", variables)

        self.create_service(data, 'all', {}, variables, controlfile_location)