예제 #1
0
def test_build_configuration_pinning(storage):
    config = dedent("""\
    jobs:
        - id: job-1
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: "original-retval"
    """)
    config = JobControlConfig.from_string(config)
    jc = JobControl(storage=storage, config=config)

    # ------------------------------------------------------------
    # Create a build with old configuration
    # ------------------------------------------------------------

    job = jc.get_job('job-1')
    build = job.create_build()
    build.run()
    build.refresh()
    assert build['finished'] and build['success']
    assert build['retval'] == 'original-retval'

    build = job.create_build()
    build_id = build.id  # Then stop using this object

    # ------------------------------------------------------------
    # Update the configuration
    # ------------------------------------------------------------

    config = dedent("""\
    jobs:
        - id: job-1
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: "new-retval"
    """)
    config = JobControlConfig.from_string(config)
    jc = JobControl(storage=storage, config=config)

    # ------------------------------------------------------------
    # Running that build will return the original return value

    build = jc.get_build(build_id)
    build.run()
    build.refresh()
    assert build['finished'] and build['success']
    assert build['retval'] == 'original-retval'

    # ------------------------------------------------------------
    # A freshly created build will return the new return value

    job = jc.get_job('job-1')
    build = job.create_build()
    build.run()
    build.refresh()
    assert build['finished'] and build['success']
    assert build['retval'] == 'new-retval'

    build = job.create_build()
    build_id = build.id  # Then stop using this object
예제 #2
0
def test_build_failure_nonserializable_exception(storage):
    """
    It only gets worse when we cannot even serialize the exception..
    But still, we can wrap it in a serialization error exception
    and be fine with it. Hopefully, we can keep the original traceback..
    """

    config = JobControlConfig.from_string("""
    jobs:
        - id: job-nse
          function: jobcontrol.utils.testing:job_raising_nonserializable
    """)
    jc = JobControl(storage=storage, config=config)

    # Run build for RAISE nonserializable
    # It should just fail with an exception in the post-run serialization
    # todo: We might even check the traceback for that..

    job = jc.get_job('job-nse')
    build = job.create_build()
    build.run()

    assert build['started']
    assert build['finished']
    assert not build['success']

    # WARNING! How to tell whether this job failed due to
    # the raised exception being serialized properly, or due
    # to the exception serialization failed?

    assert not isinstance(build['exception'], NonSerializableException)
    assert isinstance(build['exception'], ExceptionPlaceholder)
예제 #3
0
def test_config_all_keys():
    config = JobControlConfig.from_string("""
    storage: postgresql://localhost/database
    webapp:
        PORT: 5000
        HOST: 0.0.0.0
        SECRET_KEY: "super secret key"
    celery:
        BROKER_URL: redis://
    secret:
        MY_PASSWORD: '******'
    jobs:
        - id: foo
        - id: bar
    """)

    assert config.storage == 'postgresql://localhost/database'
    assert config.webapp == {
        'PORT': 5000,
        'HOST': '0.0.0.0',
        'SECRET_KEY': 'super secret key',
    }
    assert config.celery == {'BROKER_URL': 'redis://'}
    assert config.secret == {'MY_PASSWORD': '******'}
    assert len(config.jobs) == 2
    assert isinstance(config.jobs[0], BuildConfig)
    assert config.jobs[0]['id'] == 'foo'
    assert isinstance(config.jobs[1], BuildConfig)
    assert config.jobs[1]['id'] == 'bar'
예제 #4
0
def test_build_with_skip(storage):
    config = JobControlConfig.from_string("""
    jobs:
        - id: job-to-skip
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: "Foo Retval"
              skip: True
    """)
    jc = JobControl(storage=storage, config=config)

    job = jc.get_job('job-to-skip')
    build = job.create_build()
    build.run()

    assert build['started']
    assert build['finished']
    assert build['skipped']

    assert job.has_builds()
    assert not job.has_successful_builds()  # Skipped builds are ignored
    assert not job.has_running_builds()
    assert list(job.iter_builds()) == [build]
    assert list(job.iter_builds(skipped=False)) == []

    assert build['exception'] is None
    assert build['exception_tb'] is None
예제 #5
0
def test_build_with_failure(storage):
    config = JobControlConfig.from_string("""
    jobs:
        - id: foo
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: "Foo Retval"
              fail: True
    """)
    jc = JobControl(storage=storage, config=config)

    job = jc.get_job('foo')
    build = job.create_build()
    build.run()

    assert build['started']
    assert build['finished']
    assert not build['success']
    assert not build['skipped']

    assert job.has_builds()
    assert not job.has_successful_builds()
    assert not job.has_running_builds()
    assert list(job.iter_builds()) == [build]
    assert list(job.iter_builds(success=True)) == []
    assert list(job.iter_builds(success=False)) == [build]

    assert isinstance(build['exception'], RuntimeError)
    assert isinstance(build['exception_tb'], TracebackInfo)
예제 #6
0
파일: core.py 프로젝트: rshk/jobcontrol
    def from_config(cls, config):
        """
        Initialize JobControl from some configuration.

        :param config:
            Either a :py:class:`jobcontrol.config.JobControlConfig`
            instance, or a dict to be passed as argument to that
            class constructor.

        :return:
            a :py:class:`JobControl` instance
        """

        if not isinstance(config, JobControlConfig):
            config = JobControlConfig(config)
        obj = cls(storage=config.get_storage(), config=config)
        return obj
예제 #7
0
def test_config_default_values():
    config = JobControlConfig.from_string('')
    assert config.storage is None
    assert config.jobs == []
    assert config.webapp == {}
    assert config.celery == {}
    assert config.secret == {}

    assert config.get_storage() is None
예제 #8
0
파일: core.py 프로젝트: rshk/jobcontrol
    def from_config_file(cls, config_file):
        """
        Initialize JobControl by loading configuration from a file.
        Will also initialize storage taking values from the configuration.

        :param config_file:
            Path to configuration file, or an open file descriptor
            (or file-like object).

        :return:
            a :py:class:`JobControl` instance
        """

        config = JobControlConfig.from_file(config_file)
        obj = cls(storage=config.get_storage(), config=config)
        return obj
예제 #9
0
def test_simple_build_run(storage):
    config = JobControlConfig.from_string("""
    jobs:
        - id: foo
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: "Foo Retval"
    """)
    jc = JobControl(storage=storage, config=config)

    job = jc.get_job('foo')

    assert job.has_builds() is False
    assert job.has_successful_builds() is False
    assert job.has_running_builds() is False
    assert job.is_outdated() is None
    assert job.can_be_built() is True

    # Create and run a build
    # ------------------------------------------------------------

    build = job.create_build()

    assert job.has_builds() is False  # "finished" builds only
    assert job.has_successful_builds() is False
    assert job.has_running_builds() is False
    assert job.is_outdated() is None
    assert job.can_be_built() is True
    assert list(job.iter_builds()) == [build]

    build.run()

    assert build['started'] is True
    assert build['finished'] is True
    assert build['success'] is True
    assert build['skipped'] is False
    assert build['retval'] == 'Foo Retval'

    assert job.has_builds() is True
    assert job.has_successful_builds() is True
    assert job.has_running_builds() is False
    assert job.is_outdated() is False
    assert job.can_be_built() is True
    assert list(job.iter_builds()) == [build]
예제 #10
0
def test_build_failure_due_to_nonserializable_object(storage):
    config = JobControlConfig.from_string("""
    jobs:
        - id: job-nso
          function: jobcontrol.utils.testing:job_returning_nonserializable
    """)
    jc = JobControl(storage=storage, config=config)

    job = jc.get_job('job-nso')
    build = job.create_build()
    build.run()

    assert build['started']
    assert build['finished']
    assert not build['success']

    assert isinstance(build['exception'], SerializationError)
    assert (  # The original exception message is kept..
        "TypeError('a class that defines __slots__ without defining "
        "__getstate__ cannot be pickled',)") in build['exception'].message
예제 #11
0
def test_jobcontrol_config_exceptions():
    with pytest.raises(TypeError):
        JobControlConfig('This is not a dict')

    with pytest.raises(TypeError) as excinfo:
        JobControlConfig({'storage': ['not', 'a', 'string']})
    assert excinfo.value.message == 'storage must be a string'

    with pytest.raises(TypeError) as excinfo:
        JobControlConfig.from_string("""
        jobs:
            - missing: id
        """)
    assert excinfo.value.message == 'Job id cannot be None'

    with pytest.raises(ValueError) as excinfo:
        JobControlConfig.from_string("""
        jobs:
            - id: foo
            - id: bar
            - id: foo
        """)
    assert excinfo.value.message == 'Duplicate job id: foo'

    with pytest.raises(TypeError) as excinfo:
        JobControlConfig.from_string("""
        jobs:
            - id: something
              title: ['not', 'a', 'string']
        """)
    assert excinfo.value.message == 'title must be a string, got list instead'

    with pytest.raises(TypeError) as excinfo:
        JobControlConfig.from_string("""
        jobs:
            - id: something
              notes: ['not', 'a', 'string']
        """)
    assert excinfo.value.message == 'notes must be a string, got list instead'
예제 #12
0
def test_build_config_pickle_unpickle():
    import pickle

    config = JobControlConfig.from_string("""
    webapp:
        PORT: 5000
    jobs:
        - id: foo
        - id: bar
          dependencies: ['foo']
          args:
              - !retval 'foo'
    """)

    pickled_config = pickle.dumps(config)
    unpickled_config = pickle.loads(pickled_config)

    assert isinstance(unpickled_config, JobControlConfig)
    assert unpickled_config == config
    assert not (unpickled_config != config)  # test __ne__
    assert len(unpickled_config.jobs) == 2
    for job in unpickled_config.jobs:
        assert isinstance(job, BuildConfig)
예제 #13
0
def test_simple_build_deletion(storage):
    config = JobControlConfig.from_string("""
    jobs:
        - id: job-to-delete
          function: jobcontrol.utils.testing:testing_job
    """)
    jc = JobControl(storage=storage, config=config)

    job = jc.get_job('job-to-delete')

    build_1 = job.create_build()
    build_1.run()

    assert len(list(job.iter_builds())) == 1

    build_2 = job.create_build()
    build_2.run()

    assert len(list(job.iter_builds())) == 2

    build_1.delete()

    assert len(list(job.iter_builds())) == 1
예제 #14
0
def test_build_deletion_with_cleanup(storage):
    config = JobControlConfig.from_string("""
    jobs:
        - id: job-to-delete
          function: jobcontrol.utils.testing:job_creating_temp_file
          cleanup_function: jobcontrol.utils.testing:cleanup_temp_file
    """)
    jc = JobControl(storage=storage, config=config)

    job = jc.get_job('job-to-delete')

    build = job.create_build()
    build.run()

    assert build['finished'] and build['success']
    assert isinstance(build.retval, str)
    assert os.path.isfile(build.retval)

    assert len(list(job.iter_builds())) == 1

    build.delete()

    assert len(list(job.iter_builds())) == 0
    assert not os.path.isfile(build.retval)
예제 #15
0
def test_job_config_with_dependencies():
    config = JobControlConfig.from_string("""
    jobs:
        - id: foo
          function: mymodule.foo
          dependencies: []

        - id: bar
          function: mymodule.bar
          dependencies: ['foo']

        - id: baz
          function: mymodule.baz
          dependencies: ['foo', 'bar']
    """)

    # Make sure they are still the same method.
    # Note that comparing with ``is`` will fail as they are *not*
    # actually the same method anymore..
    assert config.get_job == config.get_job_config
    assert JobControlConfig.get_job == JobControlConfig.get_job_config

    assert config.get_job('foo')['dependencies'] == []
    assert config.get_job('bar')['dependencies'] == ['foo']
    assert config.get_job('baz')['dependencies'] == ['foo', 'bar']

    assert config.get_job_deps('foo') == []
    assert config.get_job_deps('bar') == ['foo']
    assert config.get_job_deps('baz') == ['foo', 'bar']

    assert config.get_job_revdeps('foo') == ['bar', 'baz']
    assert config.get_job_revdeps('bar') == ['baz']
    assert config.get_job_revdeps('baz') == []

    assert config.get_job_deps('does-not-exist') == []
    assert config.get_job_revdeps('does-not-exist') == []
예제 #16
0
def test_job_config_default_values():
    config = JobControlConfig.from_string("""
    jobs:
        - id: foo
          custom_key: 'Custom value'
    """)
    job = config.get_job('foo')
    assert isinstance(job, BuildConfig)
    assert job['id'] == 'foo'
    assert job['function'] is None
    assert job['args'] == ()
    assert job['kwargs'] == {}
    assert job['dependencies'] == []
    assert job['pinned_builds'] == {}
    assert job['title'] is None
    assert job['notes'] is None
    assert job['protected'] is False
    assert job['cleanup_function'] is None
    assert job['repr_function'] is None

    with pytest.raises(KeyError):
        job['does_not_exist']

    assert job['custom_key'] == 'Custom value'
예제 #17
0
def test_core_config_jobs(storage):
    config = JobControlConfig.from_string("""
    jobs:
        - id: foo
          function: mymodule.foo
          dependencies: []

        - id: bar
          function: mymodule.bar
          dependencies: ['foo']

        - id: baz
          function: mymodule.baz
          dependencies: ['foo', 'bar']
    """)
    jc = JobControl(storage=storage, config=config)

    job_foo = jc.get_job('foo')
    job_bar = jc.get_job('bar')
    job_baz = jc.get_job('baz')

    # Check jobs
    # ------------------------------------------------------------

    assert isinstance(job_foo, JobInfo)
    assert job_foo.id == 'foo'
    assert job_foo.config['id'] == 'foo'
    assert job_foo.config['function'] == 'mymodule.foo'
    assert job_foo.config['args'] == ()
    assert job_foo.config['kwargs'] == {}
    assert job_foo.config['dependencies'] == []
    assert list(job_foo.get_deps()) == []
    assert list(job_foo.get_revdeps()) == [job_bar, job_baz]
    assert job_foo.get_status() == 'not_built'
    assert list(job_foo.iter_builds()) == []
    assert job_foo.get_latest_successful_build() is None
    assert job_foo.has_builds() is False
    assert job_foo.has_successful_builds() is False
    assert job_foo.has_running_builds() is False
    assert job_foo.is_outdated() is None  # no builds..
    assert job_foo.can_be_built() is True

    assert isinstance(job_bar, JobInfo)
    assert job_bar.id == 'bar'
    assert job_bar.config['id'] == 'bar'
    assert job_bar.config['function'] == 'mymodule.bar'
    assert job_bar.config['args'] == ()
    assert job_bar.config['kwargs'] == {}
    assert job_bar.config['dependencies'] == ['foo']
    assert list(job_bar.get_deps()) == [job_foo]
    assert list(job_bar.get_revdeps()) == [job_baz]
    assert job_bar.get_status() == 'not_built'
    assert list(job_bar.iter_builds()) == []
    assert job_bar.get_latest_successful_build() is None
    assert job_bar.has_builds() is False
    assert job_bar.has_successful_builds() is False
    assert job_bar.has_running_builds() is False
    assert job_bar.is_outdated() is None  # no builds..
    assert job_bar.can_be_built() is False  # "foo" has no builds

    assert isinstance(job_baz, JobInfo)
    assert job_baz.id == 'baz'
    assert job_baz.config['id'] == 'baz'
    assert job_baz.config['function'] == 'mymodule.baz'
    assert job_baz.config['args'] == ()
    assert job_baz.config['kwargs'] == {}
    assert job_baz.config['dependencies'] == ['foo', 'bar']
    assert list(job_baz.get_deps()) == [job_foo, job_bar]
    assert list(job_baz.get_revdeps()) == []
    assert job_baz.get_status() == 'not_built'
    assert list(job_baz.iter_builds()) == []
    assert job_baz.get_latest_successful_build() is None
    assert job_baz.has_builds() is False
    assert job_baz.has_successful_builds() is False
    assert job_baz.has_running_builds() is False
    assert job_baz.is_outdated() is None  # no builds..
    assert job_baz.can_be_built() is False  # "foo" and "bar" have no builds

    # Exception on non-existing job

    with pytest.raises(NotFound):
        jc.get_job('does-not-exist')

    # Iterate jobs

    assert list(jc.iter_jobs()) == [job_foo, job_bar, job_baz]
예제 #18
0
def test_dependency_pinning(storage):
    # Test for dependency pinning
    # ---------------------------
    #
    # We want to make sure that a build uses the latest build for
    # a dependency at the time it was created; so if we run a build
    # for job-1, then create a build for job-2, then run another build
    # for job-1, the return values used when running a build for job-2
    # will be the one from the *first* build.
    # To ensure this, we are going to change the return value
    # in the configuration.

    config = dedent("""\
    jobs:
        - id: job-1
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: "original-retval"

        - id: job-2
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: !retval 'job-1'
          dependencies: ['job-1']
    """)

    config = JobControlConfig.from_string(config)
    jc = JobControl(storage=storage, config=config)

    build_1_1 = jc.create_build('job-1')
    build_1_1.run()
    build_1_1.refresh()
    assert build_1_1['finished'] and build_1_1['success']
    assert build_1_1['retval'] == 'original-retval'

    # This should have pinned dependency on build_1_1
    build_2_1 = jc.create_build('job-2')

    # Update configuration
    # --------------------

    config = dedent("""\
    jobs:
        - id: job-1
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: "new-retval"

        - id: job-2
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: !retval 'job-1'
          dependencies: ['job-1']
    """)

    config = JobControlConfig.from_string(config)
    jc = JobControl(storage=storage, config=config)

    build_1_2 = jc.create_build('job-1')
    build_1_2.run()
    build_1_2.refresh()
    assert build_1_2['finished'] and build_1_2['success']
    assert build_1_2['retval'] == 'new-retval'

    build_2_1 = jc.get_build(build_2_1.id)  # Get from *new* JC
    build_2_1.run()
    build_2_1.refresh()
    assert build_2_1['finished'] and build_2_1['success']
    assert build_2_1['retval'] == 'original-retval'

    build_2_2 = jc.create_build('job-2')
    build_2_2.run()
    build_2_2.refresh()
    assert build_2_2['finished'] and build_2_2['success']
    assert build_2_2['retval'] == 'new-retval'
예제 #19
0
def test_job_status_reporting(storage):

    config = JobControlConfig.from_string("""
    jobs:
        - id: job-1
          function: jobcontrol.utils.testing:testing_job
        - id: job-2
          function: jobcontrol.utils.testing:testing_job
        - id: job-3
          function: jobcontrol.utils.testing:testing_job
          dependencies: ['job-1', 'job-2']
        - id: job-4
          function: jobcontrol.utils.testing:testing_job
          dependencies: ['job-3']

    """)
    jc = JobControl(storage=storage, config=config)

    # Check status of unbuilt jobs
    job_1 = jc.get_job('job-1')
    job_2 = jc.get_job('job-2')
    job_3 = jc.get_job('job-3')
    job_4 = jc.get_job('job-4')

    assert job_1.get_status() == 'not_built'
    assert job_2.get_status() == 'not_built'
    assert job_3.get_status() == 'not_built'
    assert job_4.get_status() == 'not_built'

    assert list(job_1.iter_builds()) == []
    assert job_1.get_latest_successful_build() is None
    assert job_1.has_builds() is False
    assert job_1.has_successful_builds() is False
    assert job_1.has_running_builds() is False
    assert job_1.is_outdated() is None  # IDK
    assert job_1.can_be_built() is True

    assert job_2.can_be_built() is True
    assert job_3.can_be_built() is False  # deps not met
    assert job_4.can_be_built() is False  # deps not met

    # ------------------------------------------------------------
    # Manually start a build for job 1, as we want to
    # check it is running, etc..
    # ------------------------------------------------------------

    build_1_1 = job_1.create_build()
    assert build_1_1['started'] is False
    assert build_1_1['finished'] is False
    assert job_1.has_builds() is False
    assert job_1.has_running_builds() is False

    assert job_1.get_status() == 'not_built'

    jc.storage.start_build(build_1_1.id)
    build_1_1.refresh()
    assert build_1_1['started'] is True
    assert build_1_1['finished'] is False
    assert job_1.has_builds() is False  # **Completed** builds..
    assert job_1.has_running_builds() is True

    # Note: "running" is not anymore reported as a state
    assert job_1.get_status() == 'not_built'

    jc.storage.finish_build(build_1_1.id, success=False)
    build_1_1.refresh()
    assert build_1_1['started'] is True
    assert build_1_1['finished'] is True
    assert build_1_1['success'] is False
    assert job_1.has_builds() is True
    assert job_1.has_successful_builds() is False
    assert job_1.has_running_builds() is False

    assert job_1.get_status() == 'failed'

    # ------------------------------------------------------------
    # Do it again, with a new build, which should succeed this time
    # ------------------------------------------------------------

    build_1_2 = job_1.create_build()
    build_1_2.run()
    build_1_2.refresh()

    assert len(list(job_1.iter_builds())) == 2
    assert build_1_2['started'] is True
    assert build_1_2['finished'] is True
    assert build_1_2['success'] is True
    assert job_1.has_builds() is True
    assert job_1.has_successful_builds() is True
    assert job_1.has_running_builds() is False

    assert job_1.get_status() == 'success'

    # ------------------------------------------------------------
    # Now build job 2 and make sure 3 becomes buildable
    # ------------------------------------------------------------

    assert job_3.can_be_built() is False
    build_2_1 = job_2.create_build()
    build_2_1.run()
    assert job_3.can_be_built() is True

    # Job 4 is still missing a build from 3
    assert job_4.can_be_built() is False
    job_3.create_build().run()
    assert job_4.can_be_built() is True

    assert job_2.get_status() == 'success'
    assert job_3.get_status() == 'success'
    assert job_4.get_status() == 'not_built'

    # ------------------------------------------------------------
    # Rebuild #1 to get #3 to be "outdated"
    # ------------------------------------------------------------

    assert job_3.is_outdated() is False
    job_1.create_build().run()
    assert job_3.is_outdated() is True
    assert job_3.get_status() == 'outdated'
예제 #20
0
def test_core_config_jobs(storage):
    config = JobControlConfig.from_string("""
    jobs:
        - id: foo
          function: mymodule.foo
          dependencies: []

        - id: bar
          function: mymodule.bar
          dependencies: ['foo']

        - id: baz
          function: mymodule.baz
          dependencies: ['foo', 'bar']
    """)
    jc = JobControl(storage=storage, config=config)

    job_foo = jc.get_job('foo')
    job_bar = jc.get_job('bar')
    job_baz = jc.get_job('baz')

    # Check jobs
    # ------------------------------------------------------------

    assert isinstance(job_foo, JobInfo)
    assert job_foo.id == 'foo'
    assert job_foo.config['id'] == 'foo'
    assert job_foo.config['function'] == 'mymodule.foo'
    assert job_foo.config['args'] == ()
    assert job_foo.config['kwargs'] == {}
    assert job_foo.config['dependencies'] == []
    assert list(job_foo.get_deps()) == []
    assert list(job_foo.get_revdeps()) == [job_bar, job_baz]
    assert job_foo.get_status() == 'not_built'
    assert list(job_foo.iter_builds()) == []
    assert job_foo.get_latest_successful_build() is None
    assert job_foo.has_builds() is False
    assert job_foo.has_successful_builds() is False
    assert job_foo.has_running_builds() is False
    assert job_foo.is_outdated() is None  # no builds..
    assert job_foo.can_be_built() is True

    assert isinstance(job_bar, JobInfo)
    assert job_bar.id == 'bar'
    assert job_bar.config['id'] == 'bar'
    assert job_bar.config['function'] == 'mymodule.bar'
    assert job_bar.config['args'] == ()
    assert job_bar.config['kwargs'] == {}
    assert job_bar.config['dependencies'] == ['foo']
    assert list(job_bar.get_deps()) == [job_foo]
    assert list(job_bar.get_revdeps()) == [job_baz]
    assert job_bar.get_status() == 'not_built'
    assert list(job_bar.iter_builds()) == []
    assert job_bar.get_latest_successful_build() is None
    assert job_bar.has_builds() is False
    assert job_bar.has_successful_builds() is False
    assert job_bar.has_running_builds() is False
    assert job_bar.is_outdated() is None  # no builds..
    assert job_bar.can_be_built() is False  # "foo" has no builds

    assert isinstance(job_baz, JobInfo)
    assert job_baz.id == 'baz'
    assert job_baz.config['id'] == 'baz'
    assert job_baz.config['function'] == 'mymodule.baz'
    assert job_baz.config['args'] == ()
    assert job_baz.config['kwargs'] == {}
    assert job_baz.config['dependencies'] == ['foo', 'bar']
    assert list(job_baz.get_deps()) == [job_foo, job_bar]
    assert list(job_baz.get_revdeps()) == []
    assert job_baz.get_status() == 'not_built'
    assert list(job_baz.iter_builds()) == []
    assert job_baz.get_latest_successful_build() is None
    assert job_baz.has_builds() is False
    assert job_baz.has_successful_builds() is False
    assert job_baz.has_running_builds() is False
    assert job_baz.is_outdated() is None  # no builds..
    assert job_baz.can_be_built() is False  # "foo" and "bar" have no builds

    # Exception on non-existing job

    with pytest.raises(NotFound):
        jc.get_job('does-not-exist')

    # Iterate jobs

    assert list(jc.iter_jobs()) == [job_foo, job_bar, job_baz]
예제 #21
0
def test_job_status_reporting(storage):

    config = JobControlConfig.from_string("""
    jobs:
        - id: job-1
          function: jobcontrol.utils.testing:testing_job
        - id: job-2
          function: jobcontrol.utils.testing:testing_job
        - id: job-3
          function: jobcontrol.utils.testing:testing_job
          dependencies: ['job-1', 'job-2']
        - id: job-4
          function: jobcontrol.utils.testing:testing_job
          dependencies: ['job-3']

    """)
    jc = JobControl(storage=storage, config=config)

    # Check status of unbuilt jobs
    job_1 = jc.get_job('job-1')
    job_2 = jc.get_job('job-2')
    job_3 = jc.get_job('job-3')
    job_4 = jc.get_job('job-4')

    assert job_1.get_status() == 'not_built'
    assert job_2.get_status() == 'not_built'
    assert job_3.get_status() == 'not_built'
    assert job_4.get_status() == 'not_built'

    assert list(job_1.iter_builds()) == []
    assert job_1.get_latest_successful_build() is None
    assert job_1.has_builds() is False
    assert job_1.has_successful_builds() is False
    assert job_1.has_running_builds() is False
    assert job_1.is_outdated() is None  # IDK
    assert job_1.can_be_built() is True

    assert job_2.can_be_built() is True
    assert job_3.can_be_built() is False  # deps not met
    assert job_4.can_be_built() is False  # deps not met

    # ------------------------------------------------------------
    # Manually start a build for job 1, as we want to
    # check it is running, etc..
    # ------------------------------------------------------------

    build_1_1 = job_1.create_build()
    assert build_1_1['started'] is False
    assert build_1_1['finished'] is False
    assert job_1.has_builds() is False
    assert job_1.has_running_builds() is False

    assert job_1.get_status() == 'not_built'

    jc.storage.start_build(build_1_1.id)
    build_1_1.refresh()
    assert build_1_1['started'] is True
    assert build_1_1['finished'] is False
    assert job_1.has_builds() is False  # **Completed** builds..
    assert job_1.has_running_builds() is True

    # Note: "running" is not anymore reported as a state
    assert job_1.get_status() == 'not_built'

    jc.storage.finish_build(build_1_1.id, success=False)
    build_1_1.refresh()
    assert build_1_1['started'] is True
    assert build_1_1['finished'] is True
    assert build_1_1['success'] is False
    assert job_1.has_builds() is True
    assert job_1.has_successful_builds() is False
    assert job_1.has_running_builds() is False

    assert job_1.get_status() == 'failed'

    # ------------------------------------------------------------
    # Do it again, with a new build, which should succeed this time
    # ------------------------------------------------------------

    build_1_2 = job_1.create_build()
    build_1_2.run()
    build_1_2.refresh()

    assert len(list(job_1.iter_builds())) == 2
    assert build_1_2['started'] is True
    assert build_1_2['finished'] is True
    assert build_1_2['success'] is True
    assert job_1.has_builds() is True
    assert job_1.has_successful_builds() is True
    assert job_1.has_running_builds() is False

    assert job_1.get_status() == 'success'

    # ------------------------------------------------------------
    # Now build job 2 and make sure 3 becomes buildable
    # ------------------------------------------------------------

    assert job_3.can_be_built() is False
    build_2_1 = job_2.create_build()
    build_2_1.run()
    assert job_3.can_be_built() is True

    # Job 4 is still missing a build from 3
    assert job_4.can_be_built() is False
    job_3.create_build().run()
    assert job_4.can_be_built() is True

    assert job_2.get_status() == 'success'
    assert job_3.get_status() == 'success'
    assert job_4.get_status() == 'not_built'

    # ------------------------------------------------------------
    # Rebuild #1 to get #3 to be "outdated"
    # ------------------------------------------------------------

    assert job_3.is_outdated() is False
    job_1.create_build().run()
    assert job_3.is_outdated() is True
    assert job_3.get_status() == 'outdated'
예제 #22
0
def test_dependency_pinning(storage):
    # Test for dependency pinning
    # ---------------------------
    #
    # We want to make sure that a build uses the latest build for
    # a dependency at the time it was created; so if we run a build
    # for job-1, then create a build for job-2, then run another build
    # for job-1, the return values used when running a build for job-2
    # will be the one from the *first* build.
    # To ensure this, we are going to change the return value
    # in the configuration.

    config = dedent("""\
    jobs:
        - id: job-1
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: "original-retval"

        - id: job-2
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: !retval 'job-1'
          dependencies: ['job-1']
    """)

    config = JobControlConfig.from_string(config)
    jc = JobControl(storage=storage, config=config)

    build_1_1 = jc.create_build('job-1')
    build_1_1.run()
    build_1_1.refresh()
    assert build_1_1['finished'] and build_1_1['success']
    assert build_1_1['retval'] == 'original-retval'

    # This should have pinned dependency on build_1_1
    build_2_1 = jc.create_build('job-2')

    # Update configuration
    # --------------------

    config = dedent("""\
    jobs:
        - id: job-1
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: "new-retval"

        - id: job-2
          function: jobcontrol.utils.testing:testing_job
          kwargs:
              retval: !retval 'job-1'
          dependencies: ['job-1']
    """)

    config = JobControlConfig.from_string(config)
    jc = JobControl(storage=storage, config=config)

    build_1_2 = jc.create_build('job-1')
    build_1_2.run()
    build_1_2.refresh()
    assert build_1_2['finished'] and build_1_2['success']
    assert build_1_2['retval'] == 'new-retval'

    build_2_1 = jc.get_build(build_2_1.id)  # Get from *new* JC
    build_2_1.run()
    build_2_1.refresh()
    assert build_2_1['finished'] and build_2_1['success']
    assert build_2_1['retval'] == 'original-retval'

    build_2_2 = jc.create_build('job-2')
    build_2_2.run()
    build_2_2.refresh()
    assert build_2_2['finished'] and build_2_2['success']
    assert build_2_2['retval'] == 'new-retval'
예제 #23
0
파일: core.py 프로젝트: rshk/jobcontrol
    def __init__(self, storage, config):
        self.storage = storage

        if not isinstance(config, JobControlConfig):
            config = JobControlConfig(config)
        self.config = config