~andrewjbeach/juju-ci-tools/make-local-patcher

« back to all changes in this revision

Viewing changes to jujupy.py

  • Committer: Aaron Bentley
  • Date: 2015-07-29 13:34:36 UTC
  • mfrom: (1044.1.17 jes-support)
  • Revision ID: aaron.bentley@canonical.com-20150729133436-uvkar9z5a835prti
Support JES for deploy_job.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
import logging
15
15
import os
16
16
import re
 
17
from shutil import rmtree
17
18
import subprocess
18
19
import sys
19
20
import tempfile
62
63
        self.state = state
63
64
 
64
65
 
 
66
class JESNotSupported(Exception):
 
67
 
 
68
    def __init__(self):
 
69
        super(JESNotSupported, self).__init__(
 
70
            'This client does not support JES')
 
71
 
 
72
 
 
73
class JESByDefault(Exception):
 
74
 
 
75
    def __init__(self):
 
76
        super(JESByDefault, self).__init__(
 
77
            'This client does not need to enable JES')
 
78
 
 
79
 
65
80
def yaml_loads(yaml_str):
66
81
    return yaml.safe_load(StringIO(yaml_str))
67
82
 
274
289
            self.env.needs_sudo(), check=False, include_e=False,
275
290
            timeout=timedelta(minutes=10).total_seconds())
276
291
        if delete_jenv:
277
 
            jenv_path = get_jenv_path(get_juju_home(), self.env.environment)
 
292
            jenv_path = get_jenv_path(self.juju_home, self.env.environment)
278
293
            ensure_deleted(jenv_path)
279
294
 
280
295
    def get_juju_output(self, command, *args, **kwargs):
352
367
        if extra_env is not None:
353
368
            env.update(extra_env)
354
369
        if check:
355
 
            return subprocess.check_call(args, env=env)
356
 
        return subprocess.call(args, env=env)
 
370
            call_func = subprocess.check_call
 
371
        else:
 
372
            call_func = subprocess.call
 
373
        return call_func(args, env=env)
357
374
 
358
375
    @contextmanager
359
376
    def juju_async(self, command, args, include_e=True, timeout=None):
628
645
 
629
646
class EnvJujuClient25(EnvJujuClient):
630
647
 
 
648
    def __init__(self, *args, **kwargs):
 
649
        super(EnvJujuClient25, self).__init__(*args, **kwargs)
 
650
        self._use_jes = False
 
651
 
 
652
    def _supports_jes(self):
 
653
        commands = self.get_juju_output('help', 'commands', include_e=False)
 
654
        for line in commands.splitlines():
 
655
            if line.startswith('system'):
 
656
                return True
 
657
        return False
 
658
 
 
659
    def enable_jes(self):
 
660
        if self._use_jes:
 
661
            return
 
662
        if self._supports_jes():
 
663
            raise JESByDefault()
 
664
        self._use_jes = True
 
665
        if not self._supports_jes():
 
666
            self._use_jes = False
 
667
            raise JESNotSupported()
 
668
 
 
669
    def _get_feature_flags(self):
 
670
        if self.env.config.get('type') == 'cloudsigma':
 
671
            yield 'cloudsigma'
 
672
        if self._use_jes is True:
 
673
            yield 'jes'
 
674
 
631
675
    def _shell_environ(self):
632
676
        """Generate a suitable shell environment.
633
677
 
634
678
        Juju's directory must be in the PATH to support plugins.
635
679
        """
636
680
        env = super(EnvJujuClient25, self)._shell_environ()
637
 
        if self.env.config.get('type') == 'cloudsigma':
638
 
            env[JUJU_DEV_FEATURE_FLAGS] = 'cloudsigma'
 
681
        feature_flags = self._get_feature_flags()
 
682
        env[JUJU_DEV_FEATURE_FLAGS] = ','.join(feature_flags)
639
683
        return env
640
684
 
641
685
 
642
686
class EnvJujuClient24(EnvJujuClient25):
643
 
 
644
 
    """Currently, same feature set as juju 25"""
 
687
    """Similar to EnvJujuClient25, but lacking JES support."""
 
688
 
 
689
    def enable_jes(self):
 
690
        raise JESNotSupported()
 
691
 
 
692
    def _get_feature_flags(self):
 
693
        if self.env.config.get('type') == 'cloudsigma':
 
694
            yield 'cloudsigma'
645
695
 
646
696
 
647
697
def get_local_root(juju_home, env):
684
734
        env.config[key] = env.config.get(key, default) + 1
685
735
 
686
736
 
 
737
def dump_environments_yaml(juju_home, config):
 
738
    environments_path = get_environments_path(juju_home)
 
739
    with open(environments_path, 'w') as config_file:
 
740
        yaml.safe_dump(config, config_file)
 
741
 
 
742
 
687
743
@contextmanager
688
744
def _temp_env(new_config, parent=None, set_home=True):
689
745
    """Use the supplied config as juju environment.
692
748
    temp_bootstrap_env.
693
749
    """
694
750
    with temp_dir(parent) as temp_juju_home:
695
 
        temp_environments = get_environments_path(temp_juju_home)
696
 
        with open(temp_environments, 'w') as config_file:
697
 
            yaml.safe_dump(new_config, config_file)
 
751
        dump_environments_yaml(temp_juju_home, new_config)
698
752
        if set_home:
699
753
            context = scoped_environ()
700
754
        else:
705
759
            yield temp_juju_home
706
760
 
707
761
 
708
 
@contextmanager
709
 
def temp_bootstrap_env(juju_home, client, set_home=True):
 
762
def jes_home_path(juju_home, dir_name):
 
763
    return os.path.join(juju_home, 'jes-homes', dir_name)
 
764
 
 
765
 
 
766
@contextmanager
 
767
def make_jes_home(juju_home, dir_name, config):
 
768
    home_path = jes_home_path(juju_home, dir_name)
 
769
    if os.path.exists(home_path):
 
770
        rmtree(home_path)
 
771
    os.makedirs(home_path)
 
772
    dump_environments_yaml(home_path, config)
 
773
    yield home_path
 
774
 
 
775
 
 
776
@contextmanager
 
777
def temp_bootstrap_env(juju_home, client, set_home=True, permanent=False):
710
778
    """Create a temporary environment for bootstrapping.
711
779
 
712
780
    This involves creating a temporary juju home directory and returning its
742
810
                "/var/lib/lxc", 2000000, "LXC containers")
743
811
    new_config = {'environments': {client.env.environment: config}}
744
812
    jenv_path = get_jenv_path(juju_home, client.env.environment)
745
 
    with _temp_env(new_config, juju_home, set_home) as temp_juju_home:
 
813
    if permanent:
 
814
        context = make_jes_home(juju_home, client.env.environment, new_config)
 
815
    else:
 
816
        context = _temp_env(new_config, juju_home, set_home)
 
817
    with context as temp_juju_home:
746
818
        if os.path.lexists(jenv_path):
747
819
            raise Exception('%s already exists!' % jenv_path)
748
820
        new_jenv_path = get_jenv_path(temp_juju_home, client.env.environment)
751
823
        # partway through bootstrap.
752
824
        ensure_dir(os.path.join(juju_home, 'environments'))
753
825
        # Skip creating symlink where not supported (i.e. Windows).
754
 
        if getattr(os, 'symlink', None) is not None:
 
826
        if not permanent and getattr(os, 'symlink', None) is not None:
755
827
            os.symlink(new_jenv_path, jenv_path)
756
828
        old_juju_home = client.juju_home
757
829
        client.juju_home = temp_juju_home
758
830
        try:
759
831
            yield temp_juju_home
760
832
        finally:
761
 
            # replace symlink with file before deleting temp home.
762
 
            try:
763
 
                os.rename(new_jenv_path, jenv_path)
764
 
            except OSError as e:
765
 
                if e.errno != errno.ENOENT:
766
 
                    raise
767
 
                # Remove dangling symlink
768
 
                os.unlink(jenv_path)
769
 
            client.juju_home = old_juju_home
 
833
            if not permanent:
 
834
                # replace symlink with file before deleting temp home.
 
835
                try:
 
836
                    os.rename(new_jenv_path, jenv_path)
 
837
                except OSError as e:
 
838
                    if e.errno != errno.ENOENT:
 
839
                        raise
 
840
                    # Remove dangling symlink
 
841
                    try:
 
842
                        os.unlink(jenv_path)
 
843
                    except OSError as e:
 
844
                        if e.errno != errno.ENOENT:
 
845
                            raise
 
846
                client.juju_home = old_juju_home
770
847
 
771
848
 
772
849
class Status: