示例#1
0
def test_content_large_diff_logged(output, root):
    path = "path"
    p = Content(path, content="\n".join(["asdf"] * 21))
    p._max_diff = 20
    p._max_diff_lead = 5
    root.component += p
    with open(p.path, "w") as f:
        f.write("\n".join(["bsdf"] * 21))
    root.component.deploy()
    log = os.listdir(p.diff_dir)[0]
    with open(os.path.join(p.diff_dir, log)) as f:
        assert (f.read() == """\
---
+++
@@ -1,21 +1,21 @@
""" + "\n".join(["-bsdf"] * 21) + "\n" + "\n".join(["+asdf"] * 21) + "\n")

    assert output.backend.output == Ellipsis("""\
host > MyComponent > Content('work/mycomponent/path')
More than 20 lines of diff. Showing first and last 5 lines.
see ... for the full diff.
  path ---
  path +++
  path @@ -1,21 +1,21 @@
  path -bsdf
  path -bsdf
  path ...
  path +asdf
  path +asdf
  path +asdf
  path +asdf
  path +asdf
""")
示例#2
0
def test_yaml_diff(root):
    from batou import output
    from batou._output import TestBackend

    output.backend = TestBackend()

    p = YAMLContent("target.yaml", data={"asdf": 1, "bsdf": 2})
    root.component += p

    with open(p.path, "w") as f:
        assert f.write(json.dumps({"bsdf": 2}, sort_keys=True, indent=4))

    p.deploy()

    # fmt: off
    assert output.backend.output == Ellipsis("""\
host > MyComponent > YAMLContent(\'work/mycomponent/target.yaml\')
  target.yaml ---
  target.yaml +++
  target.yaml @@ -1,3 +1,2 @@
  target.yaml -{
  target.yaml -    "bsdf": 2
  target.yaml -}
  target.yaml +asdf: 1
  target.yaml +bsdf: 2
""")
示例#3
0
def test_example_errors_late():
    os.chdir("examples/errors2")
    out, _ = cmd("./batou deploy errors", acceptable_returncodes=[1])

    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing ===================================
main: Loading environment `errors`...
main: Verifying repository ...
main: Loading secrets ...
================================ Connecting ... ================================
localhost: Connecting via local (1/1)
============================ Configuring model ... =============================

ERROR: Trying to access address family IPv6 which is not configured for localhost:22.
      Hint: Use `require_v6=True` when instantiating the Address object.

ERROR: crontab@localhost: No cron jobs found.

ERROR: Failed override attribute conversion
      Host: localhost
 Attribute: Component1.do_what_is_needed
Conversion: convert_literal('false')
     Error: malformed node or string: <...Name object at 0x...>

ERROR: Failed override attribute conversion
      Host: localhost
 Attribute: DNSProblem.attribute_with_problem
Conversion: Address('localhost')
     Error: Need port for service address.

ERROR: Failed override attribute conversion
      Host: localhost
 Attribute: FileMode > File('work/filemode/new-file.txt') > Mode('new-file.txt').mode
Conversion: convert_mode('wrongmode')
     Error: Mode-string should be between `---------` and `rwxrwxrwx`.

ERROR: Overrides for undefined attributes
      Host: localhost
 Component: Component2
Attributes: this_does_not_exist, this_also_does_not_exist

ERROR: Unused provided resources
    Resource "backend" provided by component3 with value ['192.168.0.1']
    Resource "frontend" provided by component3 with value ['test00.gocept.net']
    Resource "sub" provided by component3 with value [<SubComponent (localhost) "Component3 > SubComponent('sub sub')">]

ERROR: Unsatisfied resource requirements
    Resource "application" required by component4

ERROR: Found dependency cycle
cycle1 depends on
        cycle2
cycle2 depends on
        cycle1

ERROR: 8 remaining unconfigured component(s)
======================= 10 ERRORS - CONFIGURATION FAILED =======================
===================== DEPLOYMENT FAILED (during configure) =====================
""")  # noqa: E501 line too long
示例#4
0
def test_example_ignores():
    os.chdir('examples/ignores')
    out, _ = cmd('./batou deploy ignores')
    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing =================================\
==
main: Loading environment `ignores`...
main: Verifying repository ...
main: Loading secrets ...
================================ Connecting ... ==============================\
==
localhost: Connecting via local (1/2)
otherhost: Connection ignored (2/2)
============================ Configuring model ... ===========================\
==
==================== Waiting for remaining connections ... ===================\
==
================================== Deploying =================================\
==
localhost: Scheduling component component1 ...
localhost: Skipping component fail ... (Component ignored)
otherhost: Skipping component fail2 ... (Host ignored)
============================= DEPLOYMENT FINISHED ============================\
==
""")
示例#5
0
def test_example_errors():
    os.chdir('examples/errors')
    out, _ = cmd('./batou deploy errors', acceptable_returncodes=[1])
    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing =================================\
==
main: Loading environment `errors`...
main: Verifying repository ...
main: Loading secrets ...
================================ Connecting ... ==============================\
==
localhost: Connecting via local (1/1)
============================ Configuring model ... ===========================\
==
ERROR: Failed loading component file
           File: .../component5/component.py
      Exception: invalid syntax (component.py, line 1)
ERROR: Failed loading component file
           File: .../component6/component.py
      Exception: No module named 'asdf'
ERROR: Missing component
      Component: missingcomponent
           Host: localhost
ERROR: Superfluous section in environment configuration
        Section: superfluoussection
ERROR: Override section for unknown component found
      Component: nonexisting-component-section
ERROR: crontab@localhost: No cron jobs found.
ERROR: Failed override attribute conversion
           Host: localhost
      Attribute: Component1.do_what_is_needed
     Conversion: convert_literal('false')
          Error: malformed node or string: <_ast.Name object at 0x...>
ERROR: Overrides for undefined attributes
           Host: localhost
      Component: Component2
     Attributes: this_does_not_exist
ERROR: Failed override attribute conversion
           Host: localhost
      Attribute: DNSProblem.attribute_with_problem
     Conversion: Address('localhost')
          Error: Need port for service address.
ERROR: Unused provided resources
    Resource "backend" provided by component3 with value ['192.168.0.1']
    Resource "frontend" provided by component3 with value ['test00.gocept.net']
    Resource "sub" provided by component3 with value [<SubComponent (localhost) "Component3 > SubComponent(sub sub)">]
ERROR: Unsatisfied resource requirements
    Resource "application" required by component4
ERROR: Found dependency cycle
     cycle1 depends on
             cycle2
     cycle2 depends on
             cycle1
ERROR: 6 remaining unconfigured component(s)
======================= 13 ERRORS - CONFIGURATION FAILED =====================\
==
============================== DEPLOYMENT FAILED =============================\
==
""")  # NOQA
示例#6
0
def test_json_diff(root):
    from batou import output
    from batou._output import TestBackend

    output.backend = TestBackend()

    p = JSONContent("target.json", data={"asdf": 1, "bsdf": 2})
    root.component += p

    with open(p.path, "w") as f:
        assert f.write(json.dumps({"bsdf": 2}, sort_keys=True, indent=4))

    p.deploy()

    assert output.backend.output == Ellipsis(
        """\
host > MyComponent > JSONContent('work/mycomponent/target.json')
  target.json ---
  target.json +++
  target.json @@ -1,3 +1,4 @@
  target.json  {
  target.json +    "asdf": 1,
  target.json      "bsdf": 2
  target.json  }
"""
    )
示例#7
0
def test_main_with_errors(capsys):
    os.chdir("examples/errors")

    from batou.deploy import main

    with pytest.raises(SystemExit) as r:
        main(environment='errors',
             platform=None,
             timeout=None,
             dirty=False,
             consistency_only=False,
             predict_only=False,
             jobs=None,
             provision_rebuild=False)

    assert r.value.code == 1

    out, err = capsys.readouterr()
    assert err == ''
    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing ===================================
main: Loading environment `errors`...
main: Verifying repository ...
main: Loading secrets ...

ERROR: Failed loading component file
      File: .../examples/errors/components/component5/component.py
 Exception: invalid syntax (component.py, line 1)

ERROR: Failed loading component file
      File: .../examples/errors/components/component6/component.py
 Exception: No module named 'asdf'

ERROR: Failed loading component file
      File: .../examples/errors/components/component7/component.py
 Exception: Attributes only support one of those parameters: either `default` or `default_conf_string`.

ERROR: Missing component
 Component: component7
      Host: localhost

ERROR: Missing component
 Component: missingcomponent
      Host: localhost

ERROR: Superfluous section in environment configuration
   Section: superfluoussection

ERROR: Override section for unknown component found
 Component: nonexisting-component-section

ERROR: Attribute override found both in environment and secrets
 Component: component1
 Attribute: my_attribute

ERROR: Secrets section for unknown component found
 Component: another-nonexisting-component-section
======================= DEPLOYMENT FAILED (during load) ========================
""")  # noqa: E501 line too long
示例#8
0
def test_example_errors_early():
    os.chdir("examples/errors")
    out, _ = cmd("./batou deploy errors", acceptable_returncodes=[1])
    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing ===================================
main: Loading environment `errors`...
main: Verifying repository ...
main: Loading secrets ...

ERROR: Failed loading component file
      File: .../examples/errors/components/component5/component.py
 Exception: invalid syntax (component.py, line 1)

ERROR: Failed loading component file
      File: .../examples/errors/components/component6/component.py
 Exception: No module named 'asdf'

ERROR: Missing component
 Component: missingcomponent
      Host: localhost

ERROR: Superfluous section in environment configuration
   Section: superfluoussection

ERROR: Override section for unknown component found
 Component: nonexisting-component-section

ERROR: Secrets section for unknown component found
 Component: another-nonexisting-component-section
======================= DEPLOYMENT FAILED (during load) ========================
""")
示例#9
0
def test_example_errors_late():
    os.chdir("examples/errors2")
    out, _ = cmd("./batou deploy errors", acceptable_returncodes=[1])

    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing =================================\
==
main: Loading environment `errors`...
main: Verifying repository ...
main: Loading secrets ...
================================ Connecting ... ==============================\
==
localhost: Connecting via local (1/1)
============================ Configuring model ... ===========================\
==

ERROR: crontab@localhost: No cron jobs found.

ERROR: Failed override attribute conversion
      Host: localhost
 Attribute: Component1.do_what_is_needed
Conversion: convert_literal('false')
     Error: malformed node or string: <...Name object at 0x...>

ERROR: Failed override attribute conversion
      Host: localhost
 Attribute: DNSProblem.attribute_with_problem
Conversion: Address('localhost')
     Error: Need port for service address.

ERROR: Overrides for undefined attributes
      Host: localhost
 Component: Component2
Attributes: this_does_not_exist, this_also_does_not_exist

ERROR: Unused provided resources
    Resource "backend" provided by component3 with value ['192.168.0.1']
    Resource "frontend" provided by component3 with value ['test00.gocept.net']
    Resource "sub" provided by component3 with value [<SubComponent (localhost) "Component3 > SubComponent('sub sub')">]

ERROR: Unsatisfied resource requirements
    Resource "application" required by component4

ERROR: Found dependency cycle
cycle1 depends on
        cycle2
cycle2 depends on
        cycle1

ERROR: 6 remaining unconfigured component(s)
======================= 8 ERRORS - CONFIGURATION FAILED ======================\
==
===================== DEPLOYMENT FAILED (during configure) ===================\
==
""")  # NOQA
示例#10
0
def test_example_errors_missing_environment():
    os.chdir("examples/errors")
    out, _ = cmd("./batou deploy production", acceptable_returncodes=[1])
    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing ===================================
main: Loading environment `production`...

ERROR: Missing environment
Environment: production
======================= DEPLOYMENT FAILED (during load) ========================
""")  # noqa: E501 line too long
示例#11
0
def test_example_errors_missing_environment():
    os.chdir('examples/errors')
    out, _ = cmd('./batou deploy production', acceptable_returncodes=[1])
    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing =================================\
==
main: Loading environment `production`...
ERROR: Missing environment
     Environment: production
============================== DEPLOYMENT FAILED =============================\
==
""")  # NOQA
示例#12
0
def test_yaml_diff_not_for_sensitive(output, root):
    p = YAMLContent("target.yaml",
                    data={
                        "asdf": 1,
                        "bsdf": 2
                    },
                    sensitive_data=True)
    root.component += p

    with open(p.path, "w") as f:
        assert f.write(json.dumps({"bsdf": 2}, sort_keys=True, indent=4))

    p.deploy()

    assert output.backend.output == Ellipsis("""\
host > MyComponent > YAMLContent('work/mycomponent/target.yaml')
Not showing diff as it contains sensitive data.
""")
示例#13
0
def test_example_errors_gpg_cannot_decrypt(monkeypatch):
    monkeypatch.setitem(os.environ, "GNUPGHOME", '')
    os.chdir("examples/errors")
    out, _ = cmd("./batou deploy errors", acceptable_returncodes=[1])
    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing ===================================
main: Loading environment `errors`...
main: Verifying repository ...
main: Loading secrets ...

ERROR: Error while calling GPG
   command: gpg --decrypt environments/errors/secrets.cfg
 exit code: 2
   message:
gpg: ...
...
ERROR: Failed loading component file
      File: .../examples/errors/components/component5/component.py
 Exception: invalid syntax (component.py, line 1)

ERROR: Failed loading component file
      File: .../examples/errors/components/component6/component.py
 Exception: No module named 'asdf'

ERROR: Failed loading component file
      File: .../examples/errors/components/component7/component.py
 Exception: Attributes only support one of those parameters: either `default` or `default_conf_string`.

ERROR: Missing component
 Component: component7
      Host: localhost

ERROR: Missing component
 Component: missingcomponent
      Host: localhost

ERROR: Superfluous section in environment configuration
   Section: superfluoussection

ERROR: Override section for unknown component found
 Component: nonexisting-component-section
======================= DEPLOYMENT FAILED (during load) ========================
""")  # noqa: E501 line too long
示例#14
0
def test_json_diff_not_for_sensitive(root):
    from batou import output
    from batou._output import TestBackend

    output.backend = TestBackend()

    p = JSONContent(
        "target.json", data={
            "asdf": 1,
            "bsdf": 2}, sensitive_data=True)
    root.component += p

    with open(p.path, "w") as f:
        assert f.write(json.dumps({"bsdf": 2}, sort_keys=True, indent=4))

    p.deploy()

    assert output.backend.output == Ellipsis("""\
host > MyComponent > JSONContent('work/mycomponent/target.json')
Not showing diff as it contains sensitive data.
""")
示例#15
0
def test_diff_is_not_shown_for_keys_in_secrets(tmp_path, monkeypatch, capsys):
    """It does not render diffs for files which contain secrets.

    Secrets might be in the config file in secrets/ or additional encrypted
    files belonging to the environment.
    """
    monkeypatch.chdir('examples/tutorial-secrets')
    if os.path.exists('work'):
        shutil.rmtree('work')
    try:
        out, _ = cmd("./batou deploy tutorial")
    finally:
        shutil.rmtree('work')
    assert out == Ellipsis("""\
batou/2... (cpython 3...)
================================== Preparing ===================================
main: Loading environment `tutorial`...
main: Verifying repository ...
main: Loading secrets ...
================================ Connecting ... ================================
localhost: Connecting via local (1/1)
============================ Configuring model ... =============================
==================== Waiting for remaining connections ... =====================
================================== Deploying ===================================
localhost: Scheduling component hello ...
localhost > Hello > File('work/hello/hello') > Presence('hello')
localhost > Hello > File('work/hello/hello') > Content('hello')
Not showing diff as it contains sensitive data,
see ...diff for the diff.
localhost > Hello > File('work/hello/other-secrets.yaml') > Presence('other-secrets.yaml')
localhost > Hello > File('work/hello/other-secrets.yaml') > Content('other-secrets.yaml')
Not showing diff as it contains sensitive data,
see ...diff for the diff.
=================================== Summary ====================================
============================= DEPLOYMENT FINISHED ==============================
""")  # noqa: E501 line too long