Beispiel #1
0
    def provision(self):
        """Provision any resources needed for the policy."""
        parameters = self.get_parameters()
        group_name = parameters['servicePlanName']['value']
        webapp_name = parameters['name']['value']

        existing_webapp = self.template_util.resource_exist(
            group_name, webapp_name)

        if not existing_webapp:
            self.template_util.create_resource_group(
                group_name, {'location': parameters['location']['value']})

            self.template_util.deploy_resource_template(
                group_name, 'dedicated_functionapp.json', parameters).wait()
        else:
            self.log.info("Found existing App %s (%s) in group %s" %
                          (webapp_name, existing_webapp.location, group_name))

        self.log.info("Building function package for %s" % webapp_name)

        archive = FunctionPackage(self.policy.data)
        archive.build()

        if archive.status(webapp_name):
            archive.publish(webapp_name)
        else:
            self.log.error(
                "Aborted deployment, ensure Application Service is healthy.")
Beispiel #2
0
    def test_event_package_files(self, session_mock):
        p = self.load_policy({
            'name': 'test-azure-package',
            'resource': 'azure.resourcegroup',
            'mode': {
                'type': FUNCTION_EVENT_TRIGGER_MODE,
                'events': ['VmWrite']
            },
        })

        packer = FunctionPackage(p.data['name'])

        packer._add_functions_required_files(p.data, 'test-queue')
        files = packer.pkg._zip_file.filelist

        self.assertTrue(
            FunctionPackageTest._file_exists(files,
                                             'test-azure-package/function.py'))
        self.assertTrue(
            FunctionPackageTest._file_exists(files,
                                             'test-azure-package/__init__.py'))
        self.assertTrue(
            FunctionPackageTest._file_exists(
                files, 'test-azure-package/function.json'))
        self.assertTrue(
            FunctionPackageTest._file_exists(files,
                                             'test-azure-package/config.json'))
        self.assertTrue(FunctionPackageTest._file_exists(files, 'host.json'))
        self.assertTrue(
            FunctionPackageTest._file_exists(files, 'extensions.csproj'))
        self.assertTrue(
            FunctionPackageTest._file_exists(files, 'bin/extensions.dll'))
    def test_package_build_cache(self, _1, rmtree_mock, add_files_mock):
        cache_zip = os.path.join(test_files_folder, 'cache.zip')

        self._create_patch(
            'c7n_azure.dependency_manager.DependencyManager.check_cache',
            return_value=True)

        functions = [('prepare_non_binary_wheels', None),
                     ('download_wheels', None), ('install_wheels', None),
                     ('create_cache_metadata', None)]
        mocks = []
        for f in functions:
            mocks.append(
                self._create_patch(
                    'c7n_azure.dependency_manager.DependencyManager.' + f[0],
                    return_value=f[1]))
        add_modules_mock = self._create_patch(
            'c7n_azure.function_package.AzurePythonPackageArchive.add_modules')
        self._create_patch(
            'c7n_azure.function_package.AzurePythonPackageArchive.__init__')

        packer = FunctionPackage('test')
        packer.build({}, [], [], [], 'queue')

        for m in mocks:
            self.assertEqual(m.call_count, 0)

        add_files_mock.assert_called_once()

        self.assertEqual(rmtree_mock.call_count, 0)
        self.assertEqual(add_modules_mock.call_count, 1)
        self.assertFalse(os.path.exists(cache_zip))
    def test_event_package_files(self, session_mock):
        p = self.load_policy({
            'name': 'test-azure-package',
            'resource': 'azure.resourcegroup',
            'mode': {
                'type': FUNCTION_EVENT_TRIGGER_MODE,
                'events': ['ResourceGroupWrite']
            },
        })

        packer = FunctionPackage(p.data['name'])
        packer.pkg = AzurePythonPackageArchive()

        packer._add_functions_required_files(p.data, 'c7n-azure==1.0',
                                             'test-queue')
        files = packer.pkg._zip_file.filelist

        self.assertTrue(
            FunctionPackageTest._file_exists(files,
                                             'test-azure-package/function.py'))
        self.assertTrue(
            FunctionPackageTest._file_exists(files,
                                             'test-azure-package/__init__.py'))
        self.assertTrue(
            FunctionPackageTest._file_exists(
                files, 'test-azure-package/function.json'))
        self.assertTrue(
            FunctionPackageTest._file_exists(files,
                                             'test-azure-package/config.json'))
        self.assertTrue(FunctionPackageTest._file_exists(files, 'host.json'))
        self.assertTrue(
            FunctionPackageTest._file_exists(files, 'requirements.txt'))
    def test_auth_file_user_assigned_identity(self):
        p = self.load_policy({
            'name': 'test-azure-public-ip',
            'resource': 'azure.publicip',
            'mode': {
                'type': FUNCTION_EVENT_TRIGGER_MODE,
                'provision-options': {
                    'identity': {
                        'type': 'SystemAssigned'
                    }
                },
                'events': ['PublicIpWrite']
            }
        })
        packer = FunctionPackage(p.data['name'])
        packer.pkg = AzurePythonPackageArchive()
        packer._add_functions_required_files(p.data, 'c7n-azure==1.0',
                                             'test-queue')

        packer.pkg.close()
        with zipfile.ZipFile(packer.pkg.path) as zf:
            content = json.loads(zf.read('test-azure-public-ip/auth.json'))
            self.assertEqual(content, {
                'subscription_id': None,
                'use_msi': True
            })
    def test_publish_functions_package_consumption(self, _1):
        function_app_name = 'cloud-custodian-test-consumption%s' % self.subscription_id[
            -12:]
        parameters = FunctionAppUtilities.FunctionAppInfrastructureParameters(
            app_insights={
                'id': '',
                'resource_group_name': CONST_GROUP_NAME,
                'name': 'cloud-custodian-test'
            },
            storage_account={
                'id': '',
                'resource_group_name': CONST_GROUP_NAME,
                'name': self.storage_name
            },
            service_plan={
                'id': '',
                'resource_group_name': CONST_GROUP_NAME,
                'name': 'cloud-custodian-test',
                'sku_tier': 'dynamic'
            },
            function_app_resource_group_name=CONST_GROUP_NAME,
            function_app_name=function_app_name)

        package = FunctionPackage("TestPolicy")
        package.pkg = AzurePythonPackageArchive()
        package.close()

        FunctionAppUtilities.publish_functions_package(parameters, package)

        # verify app setting updated
        wc = self.session.client('azure.mgmt.web.WebSiteManagementClient')
        app_settings = wc.web_apps.list_application_settings(
            CONST_GROUP_NAME, function_app_name)
        self.assertIsNotNone(
            app_settings.properties['WEBSITE_RUN_FROM_PACKAGE'])
    def test_publish_functions_package_dedicated(self, _1):
        parameters = FunctionAppUtilities.FunctionAppInfrastructureParameters(
            app_insights={
                'id': '',
                'resource_group_name': CONST_GROUP_NAME,
                'name': 'cloud-custodian-test'
            },
            storage_account={
                'id': '',
                'resource_group_name': CONST_GROUP_NAME,
                'name': self.storage_name
            },
            service_plan={
                'id': '',
                'resource_group_name': CONST_GROUP_NAME,
                'name': 'cloud-custodian-test',
                'sku_tier': 'Basic'
            },
            function_app={
                'resource_group_name': CONST_GROUP_NAME,
                'name': self.dedicated_function_name})

        package = FunctionPackage("TestPolicy")
        package.pkg = AzurePythonPackageArchive()
        package.close()

        FunctionAppUtilities.publish_functions_package(parameters, package)
    def test_package_build_no_cache(self, rmtree_mock, add_files_mock):
        functions = [('check_cache', False),
                     ('prepare_non_binary_wheels', None),
                     ('download_wheels', None), ('install_wheels', None),
                     ('create_cache_metadata', None)]
        mocks = []
        for f in functions:
            mocks.append(
                self._create_patch(
                    'c7n_azure.dependency_manager.DependencyManager.' + f[0],
                    return_value=f[1]))
        add_modules_mock = self._create_patch(
            'c7n.mu.PythonPackageArchive.add_modules')
        mocks.append(
            self._create_patch('c7n.mu.PythonPackageArchive.add_file'))

        cache_zip = os.path.join(test_files_folder, 'cache.zip')
        self.addCleanup(os.remove, cache_zip)

        packer = FunctionPackage('test', cache_override_path=test_files_folder)
        packer.build({}, [], [], [], 'queue')

        for m in mocks:
            m.assert_called_once()

        add_files_mock.assert_called_once()

        self.assertEqual(rmtree_mock.call_count, 3)
        self.assertEqual(add_modules_mock.call_count, 3)
        self.assertTrue(os.path.exists(cache_zip))
Beispiel #9
0
    def provision(self):
        """Provision any resources needed for the policy."""
        existing_service_plan = self.client.app_service_plans.get(
            self.group_name, self.parameters['servicePlanName']['value'])

        if not existing_service_plan:
            self.template_util.create_resource_group(
                self.group_name, {'location': self.parameters['location']['value']})

            self.template_util.deploy_resource_template(
                self.group_name, 'dedicated_functionapp.json', self.parameters).wait()

        else:
            existing_webapp = self.client.web_apps.get(self.group_name, self.webapp_name)
            if not existing_webapp:
                functionapp_util = FunctionAppUtilities()
                functionapp_util.deploy_webapp(self.webapp_name,
                                               self.group_name, existing_service_plan,
                                               self.parameters['storageName']['value'])
            else:
                self.log.info("Found existing App %s (%s) in group %s" %
                              (self.webapp_name, existing_webapp.location, self.group_name))

        self.log.info("Building function package for %s" % self.webapp_name)

        archive = FunctionPackage(self.policy_name)
        archive.build(self.policy.data)
        archive.close()

        self.log.info("Function package built, size is %dMB" % (archive.pkg.size / (1024 * 1024)))

        if archive.wait_for_status(self.webapp_name):
            archive.publish(self.webapp_name)
        else:
            self.log.error("Aborted deployment, ensure Application Service is healthy.")
    def test_publish_functions_package_dedicated(
            self, mock_function_package_publish):
        parameters = FunctionAppUtilities.FunctionAppInfrastructureParameters(
            app_insights={
                'id': '',
                'resource_group_name': CONST_GROUP_NAME,
                'name': 'cloud-custodian-test'
            },
            storage_account={
                'id': '',
                'resource_group_name': CONST_GROUP_NAME,
                'name': self.storage_name
            },
            service_plan={
                'id': '',
                'resource_group_name': CONST_GROUP_NAME,
                'name': 'cloud-custodian-test',
                'sku_tier': 'Basic'
            },
            function_app_resource_group_name=CONST_GROUP_NAME,
            function_app_name=self.dedicated_function_name)

        FunctionAppUtilities.publish_functions_package(
            parameters, FunctionPackage("TestPolicy"))
        mock_function_package_publish.assert_called_once()
Beispiel #11
0
 def _build_functions_package(self, queue_name):
     package = FunctionPackage(self.policy_name, )
     package.build(
         self.policy.data,
         modules=['c7n', 'c7n-azure'],
         non_binary_packages=['pyyaml', 'pycparser', 'tabulate'],
         excluded_packages=['azure-cli-core', 'distlib', 'futures'],
         queue_name=queue_name)
     package.close()
     return package
    def test_no_policy_add_required_files(self, session_mock):
        """ Tools such as mailer will package with no policy """

        packer = FunctionPackage('name')
        packer.pkg = AzurePythonPackageArchive()

        packer._add_functions_required_files(None)
        files = packer.pkg._zip_file.filelist

        self.assertTrue(FunctionPackageTest._file_exists(files, 'host.json'))
    def def_cert_validation_on_by_default(self):
        p = self.load_policy({
            'name': 'test-azure-package',
            'resource': 'azure.resourcegroup',
            'mode':
                {'type': FUNCTION_EVENT_TRIGGER_MODE,
                 'events': ['VmWrite']},
        })

        packer = FunctionPackage(p.data['name'])
        self.assertTrue(packer.enable_ssl_cert)
Beispiel #14
0
    def build_functions_package(self, queue_name=None, target_subscription_ids=None):
        self.log.info("Building function package for %s" % self.function_params.function_app_name)

        package = FunctionPackage(self.policy_name, target_sub_ids=target_subscription_ids)
        package.build(self.policy.data,
                      modules=['c7n', 'c7n-azure'],
                      non_binary_packages=['pyyaml', 'pycparser', 'tabulate', 'pyrsistent'],
                      excluded_packages=['azure-cli-core', 'distlib', 'future', 'futures'],
                      queue_name=queue_name)
        package.close()

        self.log.info("Function package built, size is %dMB" % (package.pkg.size / (1024 * 1024)))
        return package
Beispiel #15
0
def build_function_package(config, function_name, sub_id):
    schedule = config.get('function_schedule', '0 */10 * * * *')

    cache_override_path = cache_path()

    function_path = function_name + "_" + sub_id

    # Build package
    package = FunctionPackage(function_name,
                              os.path.join(os.path.dirname(__file__),
                                           'function.py'),
                              target_sub_ids=[sub_id],
                              cache_override_path=cache_override_path)

    package.build(
        None,
        modules=['c7n', 'c7n-azure', 'c7n-mailer'],
        non_binary_packages=[
            'pyyaml', 'pycparser', 'tabulate', 'jmespath', 'datadog',
            'MarkupSafe', 'simplejson', 'pyrsistent'
        ],
        excluded_packages=['azure-cli-core', 'distlib', 'future', 'futures'])

    package.pkg.add_contents(function_path + '/function.json',
                             contents=package.get_function_config({
                                 'mode': {
                                     'type': 'azure-periodic',
                                     'schedule': schedule
                                 }
                             }))

    # Add mail templates
    for d in set(config['templates_folders']):
        if not os.path.exists(d):
            continue
        for t in [f for f in os.listdir(d) if os.path.splitext(f)[1] == '.j2']:
            with open(os.path.join(d, t)) as fh:
                package.pkg.add_contents(
                    function_path + '/msg-templates/%s' % t, fh.read())

    function_config = copy.deepcopy(config)

    functions_full_template_path = '/home/site/wwwroot/' + function_path + '/msg-templates/'
    function_config['templates_folders'] = [functions_full_template_path]

    package.pkg.add_contents(function_path + '/config.json',
                             contents=json.dumps(function_config))

    package.close()
    return package
    def test_add_host_config(self):
        packer = FunctionPackage('test')
        packer.pkg = AzurePythonPackageArchive()
        with patch('c7n_azure.function_package.AzurePythonPackageArchive.add_contents') as mock:
            packer._add_host_config(FUNCTION_EVENT_TRIGGER_MODE)
            mock.assert_called_once()
            self.assertEqual(mock.call_args[1]['dest'], 'host.json')
            self.assertTrue('extensionBundle' in json.loads(mock.call_args[1]['contents']))

        with patch('c7n_azure.function_package.AzurePythonPackageArchive.add_contents') as mock:
            packer._add_host_config(FUNCTION_TIME_TRIGGER_MODE)
            mock.assert_called_once()
            self.assertEqual(mock.call_args[1]['dest'], 'host.json')
            self.assertFalse('extensionBundle' in json.loads(mock.call_args[1]['contents']))
Beispiel #17
0
    def build_functions_package(self, queue_name=None, target_subscription_ids=None):
        self.log.info("Building function package for %s" % self.function_params.function_app_name)

        requirements = generate_requirements('c7n-azure',
                                             ignore=['boto3', 'botocore', 'pywin32'],
                                             exclude='c7n')
        package = FunctionPackage(self.policy_name, target_sub_ids=target_subscription_ids)
        package.build(self.policy.data,
                      modules=['c7n', 'c7n-azure'],
                      requirements=requirements,
                      queue_name=queue_name)
        package.close()

        self.log.info("Function package built, size is %dKB" % (package.pkg.size / 1024))
        return package
Beispiel #18
0
    def test_env_var_disables_cert_validation(self):
        p = self.load_policy({
            'name': 'test-azure-package',
            'resource': 'azure.resourcegroup',
            'mode': {
                'type': FUNCTION_EVENT_TRIGGER_MODE,
                'events': ['VmWrite']
            },
        })

        with patch.dict(os.environ,
                        {ENV_CUSTODIAN_DISABLE_SSL_CERT_VERIFICATION: 'YES'},
                        clear=True):
            packer = FunctionPackage(p.data['name'])
            self.assertFalse(packer.enable_ssl_cert)
    def test_add_function_config_events(self):
        p = self.load_policy({
            'name': 'test-azure-public-ip',
            'resource': 'azure.publicip',
            'mode': {
                'type': 'azure-stream'
            }
        })

        packer = FunctionPackage(p.data['name'])

        config = packer.get_function_config(p.data)

        binding = json.loads(config)

        self.assertEqual(binding['bindings'][0]['type'], 'httpTrigger')
Beispiel #20
0
    def _publish_functions_package(self, queue_name=None):
        self.log.info("Building function package for %s" %
                      self.function_app_name)

        archive = FunctionPackage(self.policy_name)
        archive.build(self.policy.data, queue_name=queue_name)
        archive.close()

        self.log.info("Function package built, size is %dMB" %
                      (archive.pkg.size / (1024 * 1024)))

        if archive.wait_for_status(self.function_app_name):
            archive.publish(self.function_app_name)
        else:
            self.log.error(
                "Aborted deployment, ensure Application Service is healthy.")
Beispiel #21
0
def build_function_package(config, function_name, sub_id):
    schedule = config.get('function_schedule', '0 */10 * * * *')

    cache_override_path = cache_path()

    function_path = function_name + "_" + sub_id

    # Build package
    package = FunctionPackage(function_name,
                              os.path.join(os.path.dirname(__file__),
                                           'function.py'),
                              target_sub_ids=[sub_id],
                              cache_override_path=cache_override_path)

    identity = jmespath.search('function_properties.identity', config)
    package.build(None,
                  modules=['c7n', 'c7n_azure', 'c7n_mailer'],
                  requirements=get_mailer_requirements(),
                  identity=identity)

    package.pkg.add_contents(function_path + '/function.json',
                             contents=package.get_function_config({
                                 'mode': {
                                     'type': 'azure-periodic',
                                     'schedule': schedule
                                 }
                             }))

    # Add mail templates
    for d in set(config['templates_folders']):
        if not os.path.exists(d):
            continue
        for t in [f for f in os.listdir(d) if os.path.splitext(f)[1] == '.j2']:
            with open(os.path.join(d, t)) as fh:
                package.pkg.add_contents(
                    function_path + '/msg-templates/%s' % t, fh.read())

    function_config = copy.deepcopy(config)

    functions_full_template_path = '/home/site/wwwroot/' + function_path + '/msg-templates/'
    function_config['templates_folders'] = [functions_full_template_path]

    package.pkg.add_contents(function_path + '/config.json',
                             contents=json.dumps(function_config))

    package.close()
    return package
    def test_add_function_config_events(self):
        p = self.load_policy({
            'name': 'test-azure-public-ip',
            'resource': 'azure.publicip',
            'mode': {
                'type': CONST_AZURE_EVENT_TRIGGER_MODE,
                'events': ['VmWrite']
            },
        })

        packer = FunctionPackage(p.data['name'])

        config = packer.get_function_config(p.data)

        binding = json.loads(config)

        self.assertEqual(binding['bindings'][0]['type'], 'httpTrigger')
    def test_add_function_config_events(self):
        p = self.load_policy({
            'name': 'test-azure-public-ip',
            'resource': 'azure.publicip',
            'mode':
                {'type': FUNCTION_EVENT_TRIGGER_MODE,
                 'events': ['PublicIpWrite']},
        })

        packer = FunctionPackage(p.data['name'])

        config = packer.get_function_config(p.data)

        binding = json.loads(config)

        self.assertEqual(binding['bindings'][0]['type'], 'queueTrigger')
        self.assertEqual(binding['bindings'][0]['connection'], 'AzureWebJobsStorage')
Beispiel #24
0
    def test_add_function_config_eventhub(self):
        p = self.load_policy({
            'name': 'test-azure-public-ip',
            'resource': 'azure.publicip',
            'mode': {
                'type': 'azure-stream'
            }
        })

        packer = FunctionPackage(p.data)
        packer.pkg = mock.MagicMock()

        packer._add_function_config()

        binding = json.loads(packer.pkg.add_contents.call_args[1]['contents'])

        self.assertEqual(binding['bindings'][0]['type'], 'eventHubTrigger')
    def test_add_function_config_periodic(self):
        p = self.load_policy({
            'name': 'test-azure-public-ip',
            'resource': 'azure.publicip',
            'mode':
                {'type': FUNCTION_TIME_TRIGGER_MODE,
                 'schedule': '0 1 0 1 1 1'}
        })

        packer = FunctionPackage(p.data['name'])

        config = packer.get_function_config(p.data)

        binding = json.loads(config)

        self.assertEqual(binding['bindings'][0]['type'], 'timerTrigger')
        self.assertEqual(binding['bindings'][0]['name'], 'input')
        self.assertEqual(binding['bindings'][0]['schedule'], '0 1 0 1 1 1')
    def test_publish(self, post_mock):
        status_mock = MagicMock()
        post_mock.return_value = status_mock
        packer = FunctionPackage('test')
        packer.pkg = AzurePythonPackageArchive()
        creds = User(publishing_user_name='user',
                     publishing_password='******',
                     scm_uri='https://uri')

        packer.publish(creds)

        post_mock.assert_called_once()
        status_mock.raise_for_status.assert_called_once()

        self.assertEqual(post_mock.call_args[0][0],
                         'https://uri/api/zipdeploy?isAsync=true&synctriggers=true')
        self.assertEqual(post_mock.call_args[1]['headers']['content-type'],
                         'application/octet-stream')
    def test_add_policy(self):
        p = self.load_policy({
            'name': 'test-azure-public-ip',
            'resource': 'azure.publicip',
            'mode':
                {'type': FUNCTION_EVENT_TRIGGER_MODE,
                 'events': ['PublicIpWrite']},
        })

        packer = FunctionPackage(p.data['name'])

        policy = json.loads(packer._get_policy(p.data))

        self.assertEqual(policy['policies'][0],
                         {u'resource': u'azure.publicip',
                          u'name': u'test-azure-public-ip',
                          u'mode': {u'type': u'azure-event-grid',
                                    u'events': [u'PublicIpWrite']}})
Beispiel #28
0
    def _publish_functions_package(self, queue_name=None):
        self.log.info("Building function package for %s" % self.function_params.function_app_name)

        archive = FunctionPackage(self.policy_name)
        archive.build(self.policy.data, queue_name=queue_name)
        archive.close()

        self.log.info("Function package built, size is %dMB" % (archive.pkg.size / (1024 * 1024)))

        client = local_session(self.policy.session_factory)\
            .client('azure.mgmt.web.WebSiteManagementClient')
        publish_creds = client.web_apps.list_publishing_credentials(
            self.function_params.function_app_resource_group_name,
            self.function_params.function_app_name).result()

        if archive.wait_for_status(publish_creds):
            archive.publish(publish_creds)
        else:
            self.log.error("Aborted deployment, ensure Application Service is healthy.")
Beispiel #29
0
    def provision(self):
        """Provision any resources needed for the policy."""
        template_util = TemplateUtilities()

        parameters = self._get_parameters(template_util)
        group_name = parameters['servicePlanName']['value']
        webapp_name = parameters['name']['value']
        policy_name = self.policy.data['name'].replace(' ', '-').lower()

        existing_service_plan = self.client.app_service_plans.get(
            group_name, parameters['servicePlanName']['value'])

        if not existing_service_plan:
            template_util.create_resource_group(
                group_name, {'location': parameters['location']['value']})

            template_util.deploy_resource_template(
                group_name, 'dedicated_functionapp.json', parameters).wait()

        else:
            existing_webapp = self.client.web_apps.get(group_name, webapp_name)
            if not existing_webapp:
                functionapp_util = FunctionAppUtilities()
                functionapp_util.deploy_webapp(
                    webapp_name, group_name, existing_service_plan,
                    parameters['storageName']['value'])
            else:
                self.log.info(
                    "Found existing App %s (%s) in group %s" %
                    (webapp_name, existing_webapp.location, group_name))

        self.log.info("Building function package for %s" % webapp_name)

        archive = FunctionPackage(policy_name)
        archive.build(self.policy.data)
        archive.close()

        if archive.wait_for_status(webapp_name):
            archive.publish(webapp_name)
        else:
            self.log.error(
                "Aborted deployment, ensure Application Service is healthy.")
Beispiel #30
0
    def test_add_function_config_periodic(self):
        p = self.load_policy({
            'name': 'test-azure-public-ip',
            'resource': 'azure.publicip',
            'mode': {
                'type': 'azure-periodic',
                'schedule': '0 1 0 0 0'
            }
        })

        packer = FunctionPackage(p.data)
        packer.pkg = mock.MagicMock()

        packer._add_function_config()

        binding = json.loads(packer.pkg.add_contents.call_args[1]['contents'])

        self.assertEqual(binding['bindings'][0]['type'], 'timerTrigger')
        self.assertEqual(binding['bindings'][0]['name'], 'input')
        self.assertEqual(binding['bindings'][0]['schedule'], '0 1 0 0 0')