regscale-cli 6.16.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of regscale-cli might be problematic. Click here for more details.
- regscale/__init__.py +1 -0
- regscale/airflow/__init__.py +9 -0
- regscale/airflow/azure/__init__.py +9 -0
- regscale/airflow/azure/cli.py +89 -0
- regscale/airflow/azure/upload_dags.py +116 -0
- regscale/airflow/click_dags.py +127 -0
- regscale/airflow/click_mixins.py +82 -0
- regscale/airflow/config.py +25 -0
- regscale/airflow/factories/__init__.py +0 -0
- regscale/airflow/factories/connections.py +58 -0
- regscale/airflow/factories/workflows.py +78 -0
- regscale/airflow/hierarchy.py +88 -0
- regscale/airflow/operators/__init__.py +0 -0
- regscale/airflow/operators/click.py +36 -0
- regscale/airflow/sensors/__init__.py +0 -0
- regscale/airflow/sensors/sql.py +107 -0
- regscale/airflow/sessions/__init__.py +0 -0
- regscale/airflow/sessions/sql/__init__.py +3 -0
- regscale/airflow/sessions/sql/queries.py +64 -0
- regscale/airflow/sessions/sql/sql_server_queries.py +248 -0
- regscale/airflow/tasks/__init__.py +0 -0
- regscale/airflow/tasks/branches.py +22 -0
- regscale/airflow/tasks/cli.py +116 -0
- regscale/airflow/tasks/click.py +73 -0
- regscale/airflow/tasks/debugging.py +9 -0
- regscale/airflow/tasks/groups.py +116 -0
- regscale/airflow/tasks/init.py +60 -0
- regscale/airflow/tasks/states.py +47 -0
- regscale/airflow/tasks/workflows.py +36 -0
- regscale/ansible/__init__.py +9 -0
- regscale/core/__init__.py +0 -0
- regscale/core/app/__init__.py +3 -0
- regscale/core/app/api.py +571 -0
- regscale/core/app/application.py +665 -0
- regscale/core/app/internal/__init__.py +136 -0
- regscale/core/app/internal/admin_actions.py +230 -0
- regscale/core/app/internal/assessments_editor.py +873 -0
- regscale/core/app/internal/catalog.py +316 -0
- regscale/core/app/internal/comparison.py +459 -0
- regscale/core/app/internal/control_editor.py +571 -0
- regscale/core/app/internal/encrypt.py +79 -0
- regscale/core/app/internal/evidence.py +1240 -0
- regscale/core/app/internal/file_uploads.py +151 -0
- regscale/core/app/internal/healthcheck.py +66 -0
- regscale/core/app/internal/login.py +305 -0
- regscale/core/app/internal/migrations.py +240 -0
- regscale/core/app/internal/model_editor.py +1701 -0
- regscale/core/app/internal/poam_editor.py +632 -0
- regscale/core/app/internal/workflow.py +105 -0
- regscale/core/app/logz.py +74 -0
- regscale/core/app/utils/XMLIR.py +258 -0
- regscale/core/app/utils/__init__.py +0 -0
- regscale/core/app/utils/api_handler.py +358 -0
- regscale/core/app/utils/app_utils.py +1110 -0
- regscale/core/app/utils/catalog_utils/__init__.py +0 -0
- regscale/core/app/utils/catalog_utils/common.py +91 -0
- regscale/core/app/utils/catalog_utils/compare_catalog.py +193 -0
- regscale/core/app/utils/catalog_utils/diagnostic_catalog.py +97 -0
- regscale/core/app/utils/catalog_utils/download_catalog.py +103 -0
- regscale/core/app/utils/catalog_utils/update_catalog.py +718 -0
- regscale/core/app/utils/catalog_utils/update_catalog_v2.py +1378 -0
- regscale/core/app/utils/catalog_utils/update_catalog_v3.py +1272 -0
- regscale/core/app/utils/catalog_utils/update_plans.py +334 -0
- regscale/core/app/utils/file_utils.py +238 -0
- regscale/core/app/utils/parser_utils.py +81 -0
- regscale/core/app/utils/pickle_file_handler.py +57 -0
- regscale/core/app/utils/regscale_utils.py +319 -0
- regscale/core/app/utils/report_utils.py +119 -0
- regscale/core/app/utils/variables.py +226 -0
- regscale/core/decorators.py +31 -0
- regscale/core/lazy_group.py +65 -0
- regscale/core/login.py +63 -0
- regscale/core/server/__init__.py +0 -0
- regscale/core/server/flask_api.py +473 -0
- regscale/core/server/helpers.py +373 -0
- regscale/core/server/rest.py +64 -0
- regscale/core/server/static/css/bootstrap.css +6030 -0
- regscale/core/server/static/css/bootstrap.min.css +6 -0
- regscale/core/server/static/css/main.css +176 -0
- regscale/core/server/static/images/regscale-cli.svg +49 -0
- regscale/core/server/static/images/regscale.svg +38 -0
- regscale/core/server/templates/base.html +74 -0
- regscale/core/server/templates/index.html +43 -0
- regscale/core/server/templates/login.html +28 -0
- regscale/core/server/templates/make_base64.html +22 -0
- regscale/core/server/templates/upload_STIG.html +109 -0
- regscale/core/server/templates/upload_STIG_result.html +26 -0
- regscale/core/server/templates/upload_ssp.html +144 -0
- regscale/core/server/templates/upload_ssp_result.html +128 -0
- regscale/core/static/__init__.py +0 -0
- regscale/core/static/regex.py +14 -0
- regscale/core/utils/__init__.py +117 -0
- regscale/core/utils/click_utils.py +13 -0
- regscale/core/utils/date.py +238 -0
- regscale/core/utils/graphql.py +254 -0
- regscale/core/utils/urls.py +23 -0
- regscale/dev/__init__.py +6 -0
- regscale/dev/analysis.py +454 -0
- regscale/dev/cli.py +235 -0
- regscale/dev/code_gen.py +492 -0
- regscale/dev/dirs.py +69 -0
- regscale/dev/docs.py +384 -0
- regscale/dev/monitoring.py +26 -0
- regscale/dev/profiling.py +216 -0
- regscale/exceptions/__init__.py +4 -0
- regscale/exceptions/license_exception.py +7 -0
- regscale/exceptions/validation_exception.py +9 -0
- regscale/integrations/__init__.py +1 -0
- regscale/integrations/commercial/__init__.py +486 -0
- regscale/integrations/commercial/ad.py +433 -0
- regscale/integrations/commercial/amazon/__init__.py +0 -0
- regscale/integrations/commercial/amazon/common.py +106 -0
- regscale/integrations/commercial/aqua/__init__.py +0 -0
- regscale/integrations/commercial/aqua/aqua.py +91 -0
- regscale/integrations/commercial/aws/__init__.py +6 -0
- regscale/integrations/commercial/aws/cli.py +322 -0
- regscale/integrations/commercial/aws/inventory/__init__.py +110 -0
- regscale/integrations/commercial/aws/inventory/base.py +64 -0
- regscale/integrations/commercial/aws/inventory/resources/__init__.py +19 -0
- regscale/integrations/commercial/aws/inventory/resources/compute.py +234 -0
- regscale/integrations/commercial/aws/inventory/resources/containers.py +113 -0
- regscale/integrations/commercial/aws/inventory/resources/database.py +101 -0
- regscale/integrations/commercial/aws/inventory/resources/integration.py +237 -0
- regscale/integrations/commercial/aws/inventory/resources/networking.py +253 -0
- regscale/integrations/commercial/aws/inventory/resources/security.py +240 -0
- regscale/integrations/commercial/aws/inventory/resources/storage.py +91 -0
- regscale/integrations/commercial/aws/scanner.py +823 -0
- regscale/integrations/commercial/azure/__init__.py +0 -0
- regscale/integrations/commercial/azure/common.py +32 -0
- regscale/integrations/commercial/azure/intune.py +488 -0
- regscale/integrations/commercial/azure/scanner.py +49 -0
- regscale/integrations/commercial/burp.py +78 -0
- regscale/integrations/commercial/cpe.py +144 -0
- regscale/integrations/commercial/crowdstrike.py +1117 -0
- regscale/integrations/commercial/defender.py +1511 -0
- regscale/integrations/commercial/dependabot.py +210 -0
- regscale/integrations/commercial/durosuite/__init__.py +0 -0
- regscale/integrations/commercial/durosuite/api.py +1546 -0
- regscale/integrations/commercial/durosuite/process_devices.py +101 -0
- regscale/integrations/commercial/durosuite/scanner.py +637 -0
- regscale/integrations/commercial/durosuite/variables.py +21 -0
- regscale/integrations/commercial/ecr.py +90 -0
- regscale/integrations/commercial/gcp/__init__.py +237 -0
- regscale/integrations/commercial/gcp/auth.py +96 -0
- regscale/integrations/commercial/gcp/control_tests.py +238 -0
- regscale/integrations/commercial/gcp/variables.py +18 -0
- regscale/integrations/commercial/gitlab.py +332 -0
- regscale/integrations/commercial/grype.py +165 -0
- regscale/integrations/commercial/ibm.py +90 -0
- regscale/integrations/commercial/import_all/__init__.py +0 -0
- regscale/integrations/commercial/import_all/import_all_cmd.py +467 -0
- regscale/integrations/commercial/import_all/scan_file_fingerprints.json +27 -0
- regscale/integrations/commercial/jira.py +1046 -0
- regscale/integrations/commercial/mappings/__init__.py +0 -0
- regscale/integrations/commercial/mappings/csf_controls.json +713 -0
- regscale/integrations/commercial/mappings/nist_800_53_r5_controls.json +1516 -0
- regscale/integrations/commercial/nessus/__init__.py +0 -0
- regscale/integrations/commercial/nessus/nessus_utils.py +429 -0
- regscale/integrations/commercial/nessus/scanner.py +416 -0
- regscale/integrations/commercial/nexpose.py +90 -0
- regscale/integrations/commercial/okta.py +798 -0
- regscale/integrations/commercial/opentext/__init__.py +0 -0
- regscale/integrations/commercial/opentext/click.py +99 -0
- regscale/integrations/commercial/opentext/scanner.py +143 -0
- regscale/integrations/commercial/prisma.py +91 -0
- regscale/integrations/commercial/qualys.py +1462 -0
- regscale/integrations/commercial/salesforce.py +980 -0
- regscale/integrations/commercial/sap/__init__.py +0 -0
- regscale/integrations/commercial/sap/click.py +31 -0
- regscale/integrations/commercial/sap/sysdig/__init__.py +0 -0
- regscale/integrations/commercial/sap/sysdig/click.py +57 -0
- regscale/integrations/commercial/sap/sysdig/sysdig_scanner.py +190 -0
- regscale/integrations/commercial/sap/tenable/__init__.py +0 -0
- regscale/integrations/commercial/sap/tenable/click.py +49 -0
- regscale/integrations/commercial/sap/tenable/scanner.py +196 -0
- regscale/integrations/commercial/servicenow.py +1756 -0
- regscale/integrations/commercial/sicura/__init__.py +0 -0
- regscale/integrations/commercial/sicura/api.py +855 -0
- regscale/integrations/commercial/sicura/commands.py +73 -0
- regscale/integrations/commercial/sicura/scanner.py +481 -0
- regscale/integrations/commercial/sicura/variables.py +16 -0
- regscale/integrations/commercial/snyk.py +90 -0
- regscale/integrations/commercial/sonarcloud.py +260 -0
- regscale/integrations/commercial/sqlserver.py +369 -0
- regscale/integrations/commercial/stig_mapper_integration/__init__.py +0 -0
- regscale/integrations/commercial/stig_mapper_integration/click_commands.py +38 -0
- regscale/integrations/commercial/stig_mapper_integration/mapping_engine.py +353 -0
- regscale/integrations/commercial/stigv2/__init__.py +0 -0
- regscale/integrations/commercial/stigv2/ckl_parser.py +349 -0
- regscale/integrations/commercial/stigv2/click_commands.py +95 -0
- regscale/integrations/commercial/stigv2/stig_integration.py +202 -0
- regscale/integrations/commercial/synqly/__init__.py +0 -0
- regscale/integrations/commercial/synqly/assets.py +46 -0
- regscale/integrations/commercial/synqly/ticketing.py +132 -0
- regscale/integrations/commercial/synqly/vulnerabilities.py +223 -0
- regscale/integrations/commercial/synqly_jira.py +840 -0
- regscale/integrations/commercial/tenablev2/__init__.py +0 -0
- regscale/integrations/commercial/tenablev2/authenticate.py +31 -0
- regscale/integrations/commercial/tenablev2/click.py +1584 -0
- regscale/integrations/commercial/tenablev2/scanner.py +504 -0
- regscale/integrations/commercial/tenablev2/stig_parsers.py +140 -0
- regscale/integrations/commercial/tenablev2/utils.py +78 -0
- regscale/integrations/commercial/tenablev2/variables.py +17 -0
- regscale/integrations/commercial/trivy.py +162 -0
- regscale/integrations/commercial/veracode.py +96 -0
- regscale/integrations/commercial/wizv2/WizDataMixin.py +97 -0
- regscale/integrations/commercial/wizv2/__init__.py +0 -0
- regscale/integrations/commercial/wizv2/click.py +429 -0
- regscale/integrations/commercial/wizv2/constants.py +1001 -0
- regscale/integrations/commercial/wizv2/issue.py +361 -0
- regscale/integrations/commercial/wizv2/models.py +112 -0
- regscale/integrations/commercial/wizv2/parsers.py +339 -0
- regscale/integrations/commercial/wizv2/sbom.py +115 -0
- regscale/integrations/commercial/wizv2/scanner.py +416 -0
- regscale/integrations/commercial/wizv2/utils.py +796 -0
- regscale/integrations/commercial/wizv2/variables.py +39 -0
- regscale/integrations/commercial/wizv2/wiz_auth.py +159 -0
- regscale/integrations/commercial/xray.py +91 -0
- regscale/integrations/integration/__init__.py +2 -0
- regscale/integrations/integration/integration.py +26 -0
- regscale/integrations/integration/inventory.py +17 -0
- regscale/integrations/integration/issue.py +100 -0
- regscale/integrations/integration_override.py +149 -0
- regscale/integrations/public/__init__.py +103 -0
- regscale/integrations/public/cisa.py +641 -0
- regscale/integrations/public/criticality_updater.py +70 -0
- regscale/integrations/public/emass.py +411 -0
- regscale/integrations/public/emass_slcm_import.py +697 -0
- regscale/integrations/public/fedramp/__init__.py +0 -0
- regscale/integrations/public/fedramp/appendix_parser.py +548 -0
- regscale/integrations/public/fedramp/click.py +479 -0
- regscale/integrations/public/fedramp/components.py +714 -0
- regscale/integrations/public/fedramp/docx_parser.py +259 -0
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +1124 -0
- regscale/integrations/public/fedramp/fedramp_common.py +3181 -0
- regscale/integrations/public/fedramp/fedramp_docx.py +388 -0
- regscale/integrations/public/fedramp/fedramp_five.py +2343 -0
- regscale/integrations/public/fedramp/fedramp_traversal.py +138 -0
- regscale/integrations/public/fedramp/import_fedramp_r4_ssp.py +279 -0
- regscale/integrations/public/fedramp/import_workbook.py +495 -0
- regscale/integrations/public/fedramp/inventory_items.py +244 -0
- regscale/integrations/public/fedramp/mappings/__init__.py +0 -0
- regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +7388 -0
- regscale/integrations/public/fedramp/mappings/fedramp_r5_params.json +8636 -0
- regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +9605 -0
- regscale/integrations/public/fedramp/mappings/system_roles.py +34 -0
- regscale/integrations/public/fedramp/mappings/user.py +175 -0
- regscale/integrations/public/fedramp/mappings/values.py +141 -0
- regscale/integrations/public/fedramp/markdown_parser.py +150 -0
- regscale/integrations/public/fedramp/metadata.py +689 -0
- regscale/integrations/public/fedramp/models/__init__.py +59 -0
- regscale/integrations/public/fedramp/models/leveraged_auth_new.py +168 -0
- regscale/integrations/public/fedramp/models/poam_importer.py +522 -0
- regscale/integrations/public/fedramp/parts_mapper.py +107 -0
- regscale/integrations/public/fedramp/poam/__init__.py +0 -0
- regscale/integrations/public/fedramp/poam/scanner.py +851 -0
- regscale/integrations/public/fedramp/properties.py +201 -0
- regscale/integrations/public/fedramp/reporting.py +84 -0
- regscale/integrations/public/fedramp/resources.py +496 -0
- regscale/integrations/public/fedramp/rosetta.py +110 -0
- regscale/integrations/public/fedramp/ssp_logger.py +87 -0
- regscale/integrations/public/fedramp/system_characteristics.py +922 -0
- regscale/integrations/public/fedramp/system_control_implementations.py +582 -0
- regscale/integrations/public/fedramp/system_implementation.py +190 -0
- regscale/integrations/public/fedramp/xml_utils.py +87 -0
- regscale/integrations/public/nist_catalog.py +275 -0
- regscale/integrations/public/oscal.py +1946 -0
- regscale/integrations/public/otx.py +169 -0
- regscale/integrations/scanner_integration.py +2692 -0
- regscale/integrations/variables.py +25 -0
- regscale/models/__init__.py +7 -0
- regscale/models/app_models/__init__.py +5 -0
- regscale/models/app_models/catalog_compare.py +213 -0
- regscale/models/app_models/click.py +252 -0
- regscale/models/app_models/datetime_encoder.py +21 -0
- regscale/models/app_models/import_validater.py +321 -0
- regscale/models/app_models/mapping.py +260 -0
- regscale/models/app_models/pipeline.py +37 -0
- regscale/models/click_models.py +413 -0
- regscale/models/config.py +154 -0
- regscale/models/email_style.css +67 -0
- regscale/models/hierarchy.py +8 -0
- regscale/models/inspect_models.py +79 -0
- regscale/models/integration_models/__init__.py +0 -0
- regscale/models/integration_models/amazon_models/__init__.py +0 -0
- regscale/models/integration_models/amazon_models/inspector.py +262 -0
- regscale/models/integration_models/amazon_models/inspector_scan.py +206 -0
- regscale/models/integration_models/aqua.py +247 -0
- regscale/models/integration_models/azure_alerts.py +255 -0
- regscale/models/integration_models/base64.py +23 -0
- regscale/models/integration_models/burp.py +433 -0
- regscale/models/integration_models/burp_models.py +128 -0
- regscale/models/integration_models/cisa_kev_data.json +19333 -0
- regscale/models/integration_models/defender_data.py +93 -0
- regscale/models/integration_models/defenderimport.py +143 -0
- regscale/models/integration_models/drf.py +443 -0
- regscale/models/integration_models/ecr_models/__init__.py +0 -0
- regscale/models/integration_models/ecr_models/data.py +69 -0
- regscale/models/integration_models/ecr_models/ecr.py +239 -0
- regscale/models/integration_models/flat_file_importer.py +1079 -0
- regscale/models/integration_models/grype_import.py +247 -0
- regscale/models/integration_models/ibm.py +126 -0
- regscale/models/integration_models/implementation_results.py +85 -0
- regscale/models/integration_models/nexpose.py +140 -0
- regscale/models/integration_models/prisma.py +202 -0
- regscale/models/integration_models/qualys.py +720 -0
- regscale/models/integration_models/qualys_scanner.py +160 -0
- regscale/models/integration_models/sbom/__init__.py +0 -0
- regscale/models/integration_models/sbom/cyclone_dx.py +139 -0
- regscale/models/integration_models/send_reminders.py +620 -0
- regscale/models/integration_models/snyk.py +155 -0
- regscale/models/integration_models/synqly_models/__init__.py +0 -0
- regscale/models/integration_models/synqly_models/capabilities.json +1 -0
- regscale/models/integration_models/synqly_models/connector_types.py +22 -0
- regscale/models/integration_models/synqly_models/connectors/__init__.py +7 -0
- regscale/models/integration_models/synqly_models/connectors/assets.py +97 -0
- regscale/models/integration_models/synqly_models/connectors/ticketing.py +583 -0
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +169 -0
- regscale/models/integration_models/synqly_models/ocsf_mapper.py +331 -0
- regscale/models/integration_models/synqly_models/param.py +72 -0
- regscale/models/integration_models/synqly_models/synqly_model.py +733 -0
- regscale/models/integration_models/synqly_models/tenants.py +39 -0
- regscale/models/integration_models/tenable_models/__init__.py +0 -0
- regscale/models/integration_models/tenable_models/integration.py +187 -0
- regscale/models/integration_models/tenable_models/models.py +513 -0
- regscale/models/integration_models/trivy_import.py +231 -0
- regscale/models/integration_models/veracode.py +217 -0
- regscale/models/integration_models/xray.py +135 -0
- regscale/models/locking.py +100 -0
- regscale/models/platform.py +110 -0
- regscale/models/regscale_models/__init__.py +67 -0
- regscale/models/regscale_models/assessment.py +570 -0
- regscale/models/regscale_models/assessment_plan.py +52 -0
- regscale/models/regscale_models/asset.py +567 -0
- regscale/models/regscale_models/asset_mapping.py +190 -0
- regscale/models/regscale_models/case.py +42 -0
- regscale/models/regscale_models/catalog.py +261 -0
- regscale/models/regscale_models/cci.py +46 -0
- regscale/models/regscale_models/change.py +167 -0
- regscale/models/regscale_models/checklist.py +372 -0
- regscale/models/regscale_models/comment.py +49 -0
- regscale/models/regscale_models/compliance_settings.py +112 -0
- regscale/models/regscale_models/component.py +412 -0
- regscale/models/regscale_models/component_mapping.py +65 -0
- regscale/models/regscale_models/control.py +38 -0
- regscale/models/regscale_models/control_implementation.py +1128 -0
- regscale/models/regscale_models/control_objective.py +261 -0
- regscale/models/regscale_models/control_parameter.py +100 -0
- regscale/models/regscale_models/control_test.py +34 -0
- regscale/models/regscale_models/control_test_plan.py +75 -0
- regscale/models/regscale_models/control_test_result.py +52 -0
- regscale/models/regscale_models/custom_field.py +245 -0
- regscale/models/regscale_models/data.py +109 -0
- regscale/models/regscale_models/data_center.py +40 -0
- regscale/models/regscale_models/deviation.py +203 -0
- regscale/models/regscale_models/email.py +97 -0
- regscale/models/regscale_models/evidence.py +47 -0
- regscale/models/regscale_models/evidence_mapping.py +40 -0
- regscale/models/regscale_models/facility.py +59 -0
- regscale/models/regscale_models/file.py +382 -0
- regscale/models/regscale_models/filetag.py +37 -0
- regscale/models/regscale_models/form_field_value.py +94 -0
- regscale/models/regscale_models/group.py +169 -0
- regscale/models/regscale_models/implementation_objective.py +335 -0
- regscale/models/regscale_models/implementation_option.py +275 -0
- regscale/models/regscale_models/implementation_role.py +33 -0
- regscale/models/regscale_models/incident.py +177 -0
- regscale/models/regscale_models/interconnection.py +43 -0
- regscale/models/regscale_models/issue.py +1176 -0
- regscale/models/regscale_models/leveraged_authorization.py +125 -0
- regscale/models/regscale_models/line_of_inquiry.py +52 -0
- regscale/models/regscale_models/link.py +205 -0
- regscale/models/regscale_models/meta_data.py +64 -0
- regscale/models/regscale_models/mixins/__init__.py +0 -0
- regscale/models/regscale_models/mixins/parent_cache.py +124 -0
- regscale/models/regscale_models/module.py +224 -0
- regscale/models/regscale_models/modules.py +191 -0
- regscale/models/regscale_models/objective.py +14 -0
- regscale/models/regscale_models/parameter.py +87 -0
- regscale/models/regscale_models/ports_protocol.py +81 -0
- regscale/models/regscale_models/privacy.py +89 -0
- regscale/models/regscale_models/profile.py +50 -0
- regscale/models/regscale_models/profile_link.py +68 -0
- regscale/models/regscale_models/profile_mapping.py +124 -0
- regscale/models/regscale_models/project.py +63 -0
- regscale/models/regscale_models/property.py +278 -0
- regscale/models/regscale_models/question.py +85 -0
- regscale/models/regscale_models/questionnaire.py +87 -0
- regscale/models/regscale_models/questionnaire_instance.py +177 -0
- regscale/models/regscale_models/rbac.py +132 -0
- regscale/models/regscale_models/reference.py +86 -0
- regscale/models/regscale_models/regscale_model.py +1643 -0
- regscale/models/regscale_models/requirement.py +29 -0
- regscale/models/regscale_models/risk.py +274 -0
- regscale/models/regscale_models/sbom.py +54 -0
- regscale/models/regscale_models/scan_history.py +436 -0
- regscale/models/regscale_models/search.py +53 -0
- regscale/models/regscale_models/security_control.py +132 -0
- regscale/models/regscale_models/security_plan.py +204 -0
- regscale/models/regscale_models/software_inventory.py +159 -0
- regscale/models/regscale_models/stake_holder.py +64 -0
- regscale/models/regscale_models/stig.py +647 -0
- regscale/models/regscale_models/supply_chain.py +152 -0
- regscale/models/regscale_models/system_role.py +188 -0
- regscale/models/regscale_models/system_role_external_assignment.py +40 -0
- regscale/models/regscale_models/tag.py +37 -0
- regscale/models/regscale_models/tag_mapping.py +19 -0
- regscale/models/regscale_models/task.py +133 -0
- regscale/models/regscale_models/threat.py +196 -0
- regscale/models/regscale_models/user.py +175 -0
- regscale/models/regscale_models/user_group.py +55 -0
- regscale/models/regscale_models/vulnerability.py +242 -0
- regscale/models/regscale_models/vulnerability_mapping.py +162 -0
- regscale/models/regscale_models/workflow.py +55 -0
- regscale/models/regscale_models/workflow_action.py +34 -0
- regscale/models/regscale_models/workflow_instance.py +269 -0
- regscale/models/regscale_models/workflow_instance_step.py +114 -0
- regscale/models/regscale_models/workflow_template.py +58 -0
- regscale/models/regscale_models/workflow_template_step.py +45 -0
- regscale/regscale.py +815 -0
- regscale/utils/__init__.py +7 -0
- regscale/utils/b64conversion.py +14 -0
- regscale/utils/click_utils.py +118 -0
- regscale/utils/decorators.py +48 -0
- regscale/utils/dict_utils.py +59 -0
- regscale/utils/files.py +79 -0
- regscale/utils/fxns.py +30 -0
- regscale/utils/graphql_client.py +113 -0
- regscale/utils/lists.py +16 -0
- regscale/utils/numbers.py +12 -0
- regscale/utils/shell.py +148 -0
- regscale/utils/string.py +121 -0
- regscale/utils/synqly_utils.py +165 -0
- regscale/utils/threading/__init__.py +8 -0
- regscale/utils/threading/threadhandler.py +131 -0
- regscale/utils/threading/threadsafe_counter.py +47 -0
- regscale/utils/threading/threadsafe_dict.py +242 -0
- regscale/utils/threading/threadsafe_list.py +83 -0
- regscale/utils/version.py +104 -0
- regscale/validation/__init__.py +0 -0
- regscale/validation/address.py +37 -0
- regscale/validation/record.py +48 -0
- regscale/visualization/__init__.py +5 -0
- regscale/visualization/click.py +34 -0
- regscale_cli-6.16.0.0.dist-info/LICENSE +21 -0
- regscale_cli-6.16.0.0.dist-info/METADATA +659 -0
- regscale_cli-6.16.0.0.dist-info/RECORD +481 -0
- regscale_cli-6.16.0.0.dist-info/WHEEL +5 -0
- regscale_cli-6.16.0.0.dist-info/entry_points.txt +6 -0
- regscale_cli-6.16.0.0.dist-info/top_level.txt +2 -0
- tests/fixtures/__init__.py +2 -0
- tests/fixtures/api.py +87 -0
- tests/fixtures/models.py +91 -0
- tests/fixtures/test_fixture.py +144 -0
- tests/mocks/__init__.py +0 -0
- tests/mocks/objects.py +3 -0
- tests/mocks/response.py +32 -0
- tests/mocks/xml.py +13 -0
- tests/regscale/__init__.py +0 -0
- tests/regscale/core/__init__.py +0 -0
- tests/regscale/core/test_api.py +232 -0
- tests/regscale/core/test_app.py +406 -0
- tests/regscale/core/test_login.py +37 -0
- tests/regscale/core/test_logz.py +66 -0
- tests/regscale/core/test_sbom_generator.py +87 -0
- tests/regscale/core/test_validation_utils.py +163 -0
- tests/regscale/core/test_version.py +78 -0
- tests/regscale/models/__init__.py +0 -0
- tests/regscale/models/test_asset.py +71 -0
- tests/regscale/models/test_config.py +26 -0
- tests/regscale/models/test_control_implementation.py +27 -0
- tests/regscale/models/test_import.py +97 -0
- tests/regscale/models/test_issue.py +36 -0
- tests/regscale/models/test_mapping.py +52 -0
- tests/regscale/models/test_platform.py +31 -0
- tests/regscale/models/test_regscale_model.py +346 -0
- tests/regscale/models/test_report.py +32 -0
- tests/regscale/models/test_tenable_integrations.py +118 -0
- tests/regscale/models/test_user_model.py +121 -0
- tests/regscale/test_about.py +19 -0
- tests/regscale/test_authorization.py +65 -0
regscale/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "6.16.0.0"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Prevent usage and imports that will break when used outside of airflow-azure extra."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from azure.storage.blob import BlobServiceClient
|
|
7
|
+
except ImportError:
|
|
8
|
+
print("To use Azure Blob Storage features, you need to install the [airflow-azure] extra package.")
|
|
9
|
+
sys.exit(1)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Provide a CLI for uploading DAGs to Azure Blob Storage."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from regscale.airflow.azure.upload_dags import (
|
|
8
|
+
upload_dag_to_blob_storage,
|
|
9
|
+
upload_dags_to_blob_storage,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group(name="dags")
|
|
14
|
+
def cli():
|
|
15
|
+
"""Upload DAGs or files to Azure Blob Storage."""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@cli.command()
|
|
20
|
+
@click.option(
|
|
21
|
+
"--file",
|
|
22
|
+
"-f",
|
|
23
|
+
"file_path",
|
|
24
|
+
required=True,
|
|
25
|
+
type=click.Path(exists=True),
|
|
26
|
+
help="Path to the DAG file to upload.",
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--conn-string",
|
|
30
|
+
"-c",
|
|
31
|
+
"connection_string",
|
|
32
|
+
default=os.getenv("AZURE_STORAGE_CONNECTION_STRING", None),
|
|
33
|
+
help="Azure Blob Storage connection string.",
|
|
34
|
+
)
|
|
35
|
+
@click.option(
|
|
36
|
+
"--container",
|
|
37
|
+
"-n",
|
|
38
|
+
"container_name",
|
|
39
|
+
default=os.getenv("AZURE_STORAGE_CONTAINER_NAME", "dags"),
|
|
40
|
+
help="Azure Blob Storage container name.",
|
|
41
|
+
)
|
|
42
|
+
def upload_dag(file_path: str, connection_string: str, container_name: str) -> None:
|
|
43
|
+
"""Upload a single DAG to Azure Blob Storage."""
|
|
44
|
+
if connection_string is None:
|
|
45
|
+
click.echo("You can set the connection string with the AZURE_STORAGE_CONNECTION_STRING environment variable.")
|
|
46
|
+
if container_name is None:
|
|
47
|
+
click.echo("You can set the container name with the AZURE_STORAGE_CONTAINER_NAME environment variable.")
|
|
48
|
+
if connection_string is None or container_name is None:
|
|
49
|
+
click.echo("Please provide the connection string and container name for Azure Blob Storage.")
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
upload_dag_to_blob_storage(
|
|
52
|
+
connection_string=connection_string,
|
|
53
|
+
container_name=container_name,
|
|
54
|
+
blob_name=file_path,
|
|
55
|
+
file_path=file_path,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@cli.command()
|
|
60
|
+
@click.option("--path", "-p", "path", default="airflow/dags/", help="Path to the DAGs folder.")
|
|
61
|
+
@click.option(
|
|
62
|
+
"--conn-string",
|
|
63
|
+
"-c",
|
|
64
|
+
"connection_string",
|
|
65
|
+
default=os.getenv("AZURE_STORAGE_CONNECTION_STRING", None),
|
|
66
|
+
help="Azure Blob Storage connection string.",
|
|
67
|
+
)
|
|
68
|
+
@click.option(
|
|
69
|
+
"--container",
|
|
70
|
+
"-n",
|
|
71
|
+
"container_name",
|
|
72
|
+
default=os.getenv("AZURE_STORAGE_CONTAINER_NAME", "dags"),
|
|
73
|
+
help="Azure Blob Storage container name.",
|
|
74
|
+
)
|
|
75
|
+
def upload_dags(path: str, connection_string: str, container_name: str) -> None:
|
|
76
|
+
"""Upload DAGs to Azure Blob Storage."""
|
|
77
|
+
if connection_string is None or container_name is None:
|
|
78
|
+
click.echo("Please provide the connection string and container name for Azure Blob Storage.")
|
|
79
|
+
if connection_string is None:
|
|
80
|
+
click.echo("You can set the connection string with the AZURE_STORAGE_CONNECTION_STRING environment variable.")
|
|
81
|
+
if container_name is None:
|
|
82
|
+
click.echo("You can set the container name with the AZURE_STORAGE_CONTAINER_NAME environment variable.")
|
|
83
|
+
if connection_string is None or container_name is None:
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
upload_dags_to_blob_storage(
|
|
86
|
+
path=path,
|
|
87
|
+
connection_string=connection_string,
|
|
88
|
+
container_name=container_name,
|
|
89
|
+
)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Provide functions and an entrypoint for uploading DAGs to Azure Blob Storage."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional, Union, List, Tuple
|
|
7
|
+
|
|
8
|
+
from azure.storage.blob import BlobServiceClient
|
|
9
|
+
|
|
10
|
+
from regscale.core.app.logz import create_logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def upload_dag_to_blob_storage(
|
|
14
|
+
connection_string: str,
|
|
15
|
+
container_name: str,
|
|
16
|
+
blob_name: str,
|
|
17
|
+
file_path: Union[str, Path],
|
|
18
|
+
exit_on_failure: bool = True,
|
|
19
|
+
) -> None:
|
|
20
|
+
"""Upload a DAG to Azure Blob Storage
|
|
21
|
+
|
|
22
|
+
:param str connection_string: Azure Blob Storage connection string
|
|
23
|
+
:param str container_name: Azure Blob Storage container name
|
|
24
|
+
:param str blob_name: Azure Blob Storage blob name
|
|
25
|
+
:param Union[str, Path] file_path: Path to the DAG file
|
|
26
|
+
:param bool exit_on_failure: Whether to exit on failure, default True
|
|
27
|
+
:rtype: None
|
|
28
|
+
"""
|
|
29
|
+
logger = create_logger()
|
|
30
|
+
if isinstance(file_path, str):
|
|
31
|
+
file_path = Path(file_path)
|
|
32
|
+
try:
|
|
33
|
+
blob_service_client = BlobServiceClient.from_connection_string(connection_string)
|
|
34
|
+
blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name)
|
|
35
|
+
blob_client.upload_blob(file_path.read_bytes(), overwrite=True)
|
|
36
|
+
logger.info(f"{file_path} with {blob_name} uploaded to Azure Blob Storage")
|
|
37
|
+
except Exception as e:
|
|
38
|
+
logger.error(f"Failed to upload DAG to Azure Blob Storage: {e}")
|
|
39
|
+
if exit_on_failure:
|
|
40
|
+
sys.exit(1)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def retrieve_dags_to_upload(path: Union[str, Path], file_extension: str = ".py") -> List[Tuple[Path, str]]:
|
|
44
|
+
"""
|
|
45
|
+
Retrieve DAGs to upload to Azure Blob Storage
|
|
46
|
+
|
|
47
|
+
:param Union[str, Path] path: Path to the DAGs folder
|
|
48
|
+
:param str file_extension: File extension of the DAGs, default .py
|
|
49
|
+
:return: List of DAGs to upload
|
|
50
|
+
:rtype: List[Tuple[Path, str]]
|
|
51
|
+
"""
|
|
52
|
+
if isinstance(path, str):
|
|
53
|
+
path = Path(path)
|
|
54
|
+
dags = path.glob(f"*{file_extension}")
|
|
55
|
+
return [(dag, dag.name) for dag in dags]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def upload_dags_to_blob_storage(
|
|
59
|
+
path: str = "airflow/dags/",
|
|
60
|
+
connection_string: Optional[str] = None,
|
|
61
|
+
container_name: Optional[str] = None,
|
|
62
|
+
exit_on_failure: bool = True,
|
|
63
|
+
) -> None:
|
|
64
|
+
"""Upload DAGs to Azure Blob Storage
|
|
65
|
+
|
|
66
|
+
:param str path: Path to the DAGs folder, default airflow/dags/
|
|
67
|
+
:param Optional[str] connection_string: Azure Blob Storage connection string
|
|
68
|
+
:param Optional[str] container_name: Azure Blob Storage container name
|
|
69
|
+
:param bool exit_on_failure: Whether to exit on failure, default True
|
|
70
|
+
:rtype: None
|
|
71
|
+
"""
|
|
72
|
+
logger = create_logger()
|
|
73
|
+
if connection_string is None:
|
|
74
|
+
connection_string = os.environ.get("AZURE_STORAGE_CONNECTION_STRING")
|
|
75
|
+
if container_name is None:
|
|
76
|
+
container_name = os.environ.get("AZURE_STORAGE_CONTAINER_NAME")
|
|
77
|
+
if connection_string is None or container_name is None:
|
|
78
|
+
logger.error("Please provide the connection string and container name for Azure Blob Storage.")
|
|
79
|
+
if exit_on_failure:
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
dags = retrieve_dags_to_upload(path)
|
|
82
|
+
for dag, blob_name in dags:
|
|
83
|
+
upload_dag_to_blob_storage(
|
|
84
|
+
connection_string=connection_string,
|
|
85
|
+
container_name=container_name,
|
|
86
|
+
blob_name=blob_name,
|
|
87
|
+
file_path=dag,
|
|
88
|
+
exit_on_failure=exit_on_failure,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def main():
|
|
93
|
+
"""Entrypoint for uploading DAGs to Azure Blob Storage."""
|
|
94
|
+
logger = create_logger()
|
|
95
|
+
if len(sys.argv) == 2:
|
|
96
|
+
connection_string = sys.argv[1]
|
|
97
|
+
container_name = sys.argv[2]
|
|
98
|
+
else:
|
|
99
|
+
connection_string = os.environ.get("ADS_CONN_STRING")
|
|
100
|
+
container_name = os.environ.get("AZURE_STORAGE_CONTAINER_NAME", "dags")
|
|
101
|
+
if connection_string is None or container_name is None:
|
|
102
|
+
logger.error("Please provide the connection string and container name for Azure Blob Storage.")
|
|
103
|
+
if connection_string is None:
|
|
104
|
+
logger.error("Connection string not provided.")
|
|
105
|
+
if container_name is None:
|
|
106
|
+
logger.error("Container name not provided.")
|
|
107
|
+
sys.exit(1)
|
|
108
|
+
upload_dags_to_blob_storage(
|
|
109
|
+
connection_string=connection_string,
|
|
110
|
+
container_name=container_name,
|
|
111
|
+
exit_on_failure=False,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
if __name__ == "__main__":
|
|
116
|
+
main()
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Create functions to generate a DAG based on click hierarchy."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from airflow.operators.python import PythonOperator
|
|
9
|
+
|
|
10
|
+
from airflow import DAG
|
|
11
|
+
from regscale.airflow.tasks.cli import make_login_task, make_set_domain_task
|
|
12
|
+
from regscale.airflow.tasks.click import execute_click_command
|
|
13
|
+
from regscale.models.click_models import ClickCommand
|
|
14
|
+
from regscale.regscale import cli
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def generate_operator_for_command(
|
|
18
|
+
command: ClickCommand,
|
|
19
|
+
dag: DAG,
|
|
20
|
+
command_name: str = None,
|
|
21
|
+
) -> PythonOperator:
|
|
22
|
+
"""Generate an Operator for click command
|
|
23
|
+
|
|
24
|
+
:param ClickCommand command: a click.Command instruction
|
|
25
|
+
:param DAG dag: an Airflow DAG
|
|
26
|
+
:param str command_name: an optional string for command_name, will default to click.Command.name
|
|
27
|
+
:return: A PythonOperator configured with the click command.name
|
|
28
|
+
:rtype: PythonOperator
|
|
29
|
+
"""
|
|
30
|
+
return PythonOperator(
|
|
31
|
+
task_id=command_name or command.name, # TODO - also name based on group id
|
|
32
|
+
python_callable=execute_click_command,
|
|
33
|
+
op_kwargs={"command": command},
|
|
34
|
+
dag=dag,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def generate_dag_for_group(
|
|
39
|
+
group: click.Group,
|
|
40
|
+
default_args: dict,
|
|
41
|
+
command_name: str = None,
|
|
42
|
+
) -> DAG:
|
|
43
|
+
"""Generate a dag for a click group
|
|
44
|
+
|
|
45
|
+
:param click.Group group: a click Group object to generate dags for
|
|
46
|
+
:param dict default_args: dict to be passed when creating the DAGs
|
|
47
|
+
:param str command_name: an optional string for command_name, will default to click.Command.name
|
|
48
|
+
:return: a dag for each click group
|
|
49
|
+
:rtype: DAG
|
|
50
|
+
"""
|
|
51
|
+
if command_name is None:
|
|
52
|
+
command_name = group.name
|
|
53
|
+
dag = DAG(
|
|
54
|
+
command_name,
|
|
55
|
+
default_args=default_args,
|
|
56
|
+
description=f"DAG for Click Group: {group.name}",
|
|
57
|
+
schedule_interval=None,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# iterate through the group value commands
|
|
61
|
+
for command in group.commands.values():
|
|
62
|
+
if isinstance(command, click.Group):
|
|
63
|
+
# if it is click.Group, call this recursively, adding to the command_name
|
|
64
|
+
generate_dag_for_group(
|
|
65
|
+
command,
|
|
66
|
+
default_args,
|
|
67
|
+
command_name="__".join([command_name, command.name]),
|
|
68
|
+
)
|
|
69
|
+
else:
|
|
70
|
+
login_task = make_login_task(dag=dag)
|
|
71
|
+
domain_task = make_set_domain_task(dag=dag)
|
|
72
|
+
operator = generate_operator_for_command(
|
|
73
|
+
command=command,
|
|
74
|
+
dag=dag,
|
|
75
|
+
command_name="__".join([command_name, command.name]),
|
|
76
|
+
)
|
|
77
|
+
# assign the relationships for the operators
|
|
78
|
+
domain_task >> login_task >> operator
|
|
79
|
+
return dag
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def generate_dags_for_click_app(
|
|
83
|
+
app: Optional[click.Group] = None,
|
|
84
|
+
default_args: Optional[dict] = None,
|
|
85
|
+
command_name: Optional[str] = None,
|
|
86
|
+
) -> list:
|
|
87
|
+
"""Generate DAGs for a click app
|
|
88
|
+
|
|
89
|
+
:param Optional[click.Group] app: a click app to generate dags for, defaults to None
|
|
90
|
+
:param Optional[dict] default_args: dict to be passed when creating the DAGs, defaults to None
|
|
91
|
+
:param Optional[str] command_name: the name of the command to generate the DAG for, defaults to None
|
|
92
|
+
:return: a list of DAGs for the click app
|
|
93
|
+
:rtype: list
|
|
94
|
+
"""
|
|
95
|
+
# if app is not passed, use the cli
|
|
96
|
+
if app is None:
|
|
97
|
+
app = cli
|
|
98
|
+
if not default_args:
|
|
99
|
+
default_args = dict(
|
|
100
|
+
owner=os.getenv("REGSCALE_AIRFLOW_USER", "regscale"),
|
|
101
|
+
start_date=datetime.now(timezone.utc),
|
|
102
|
+
)
|
|
103
|
+
if not command_name:
|
|
104
|
+
command_name = "regscale" if app.name == "cli" else app.name
|
|
105
|
+
dags = []
|
|
106
|
+
for group in app.commands.values():
|
|
107
|
+
if isinstance(group, click.Group):
|
|
108
|
+
command_name = "regscale" if group.name == "cli" else group.name
|
|
109
|
+
for sub_group in group.commands.values():
|
|
110
|
+
if isinstance(sub_group, click.Group):
|
|
111
|
+
generate_dag_for_group(app=sub_group, default_args=default_args)
|
|
112
|
+
else:
|
|
113
|
+
dag = generate_dag_for_group(
|
|
114
|
+
group=group,
|
|
115
|
+
default_args=default_args,
|
|
116
|
+
command_name=command_name,
|
|
117
|
+
)
|
|
118
|
+
dags.append(dag)
|
|
119
|
+
elif isinstance(group, click.Command):
|
|
120
|
+
dag = generate_dag_for_group(
|
|
121
|
+
group=group,
|
|
122
|
+
default_args=default_args,
|
|
123
|
+
command_name=command_name,
|
|
124
|
+
)
|
|
125
|
+
dags.append(dag)
|
|
126
|
+
|
|
127
|
+
return dags
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Provide mixins for click models."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
from airflow.operators.python import PythonOperator
|
|
6
|
+
|
|
7
|
+
from regscale.models.click_models import ClickGroup, ClickCommand
|
|
8
|
+
from regscale.airflow.tasks.click import execute_click_command
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AirflowOperatorMixin:
|
|
12
|
+
"""Mixin to the ClickGroup to flatten for an Airflow Operator"""
|
|
13
|
+
|
|
14
|
+
def flatten_operator(self):
|
|
15
|
+
"""Flatten the group to a dictionary of PythonOperator objects"""
|
|
16
|
+
operators_ = {}
|
|
17
|
+
|
|
18
|
+
def _flatten_operator(group_: Any, prefix: str = "") -> dict:
|
|
19
|
+
"""Flatten the group to a dictionary of PythonOperator objects
|
|
20
|
+
|
|
21
|
+
:param Any group_: a click.Group object to generate dags for
|
|
22
|
+
:param str prefix: a string to prefix the command name with, defaults to ""
|
|
23
|
+
:return: a dictionary of PythonOperator objects
|
|
24
|
+
:rtype: dict
|
|
25
|
+
"""
|
|
26
|
+
for name, cmd in group_.commands.items():
|
|
27
|
+
if isinstance(cmd, ClickCommand):
|
|
28
|
+
cmd_name = cmd.name if prefix == "" else f"{prefix}__{cmd.name}"
|
|
29
|
+
|
|
30
|
+
def _make_operator_wrapper(
|
|
31
|
+
cmd_name_: str, cmd_: ClickCommand, suffix: str = None, **kwargs
|
|
32
|
+
) -> PythonOperator:
|
|
33
|
+
"""Create a wrapper to make a python operator."""
|
|
34
|
+
if suffix is None:
|
|
35
|
+
suffix = str(uuid4())[:8]
|
|
36
|
+
op_kwargs = None
|
|
37
|
+
if "op_kwargs" in kwargs:
|
|
38
|
+
op_kwargs = kwargs.pop("op_kwargs")
|
|
39
|
+
inputs = dict(
|
|
40
|
+
task_id=f"{cmd_name_}-{suffix}",
|
|
41
|
+
python_callable=execute_click_command,
|
|
42
|
+
)
|
|
43
|
+
if kwargs:
|
|
44
|
+
inputs |= kwargs
|
|
45
|
+
if op_kwargs:
|
|
46
|
+
inputs |= {"op_kwargs": {"command": cmd_, **op_kwargs}}
|
|
47
|
+
else:
|
|
48
|
+
inputs |= {"op_kwargs": {"command": cmd_}}
|
|
49
|
+
return PythonOperator(**inputs)
|
|
50
|
+
|
|
51
|
+
def _construct_lambda_wrapper(cmd_name_, cmd_, suffix: str = None, **kwargs):
|
|
52
|
+
return _make_operator_wrapper(cmd_name_=cmd_name_, cmd_=cmd_, suffix=suffix, **kwargs)
|
|
53
|
+
|
|
54
|
+
operators_[cmd_name] = {
|
|
55
|
+
"operator": PythonOperator(
|
|
56
|
+
task_id=cmd_name,
|
|
57
|
+
python_callable=execute_click_command,
|
|
58
|
+
op_kwargs={"command": cmd},
|
|
59
|
+
),
|
|
60
|
+
"lambda": lambda cmd_name_=cmd_name, cmd_=cmd, suffix=None, **kwargs: _construct_lambda_wrapper(
|
|
61
|
+
cmd_name_=cmd_name_, cmd_=cmd_, suffix=suffix, **kwargs
|
|
62
|
+
),
|
|
63
|
+
"command": cmd,
|
|
64
|
+
}
|
|
65
|
+
elif isinstance(cmd, ClickGroup):
|
|
66
|
+
new_prefix = f"{prefix}__{cmd.group_name}" if prefix else cmd.group_name
|
|
67
|
+
_flatten_operator(cmd, new_prefix)
|
|
68
|
+
|
|
69
|
+
_flatten_operator(self)
|
|
70
|
+
return operators_
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class AirflowClickGroup(AirflowOperatorMixin, ClickGroup):
|
|
74
|
+
"""Initialize the AirflowClickGroup object."""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# here's an example of how to generate the OPERATORS AirflowClickGroup class
|
|
78
|
+
if __name__ == "__main__":
|
|
79
|
+
from regscale.regscale import cli
|
|
80
|
+
|
|
81
|
+
group = AirflowClickGroup.from_group(cli, prefix="regscale")
|
|
82
|
+
operators = group.flatten_operator()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Provide configurations for Airflow."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def yesterday():
|
|
7
|
+
"""Return yesterday from now in datetime"""
|
|
8
|
+
return datetime.now() - timedelta(days=1)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
DEFAULT_ARGS = {
|
|
12
|
+
"owner": "airflow",
|
|
13
|
+
"depends_on_past": False,
|
|
14
|
+
# 'start_date': (datetime.now() - timedelta(days=1)).date(), # left here to show we intentionally disable
|
|
15
|
+
"email": ["airflow@regscale.com"],
|
|
16
|
+
"email_on_failure": False,
|
|
17
|
+
"email_on_retry": False,
|
|
18
|
+
"retries": 2,
|
|
19
|
+
"retry_delay": timedelta(minutes=2),
|
|
20
|
+
"execution_timeout": timedelta(hours=3),
|
|
21
|
+
# 'queue': 'whatever queue we want to implement', # left here for an example
|
|
22
|
+
# 'pool': 'backfill', # another example default arg
|
|
23
|
+
# 'priority_weight': 10, # give this a high priority weight
|
|
24
|
+
# 'end_date': datetime(2038, 1, 1), # left to show that end dates can be set
|
|
25
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Provide connection management functions."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from airflow.models import Connection
|
|
7
|
+
from airflow.operators.python import PythonOperator
|
|
8
|
+
|
|
9
|
+
from airflow import settings
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_connection_operator(
|
|
13
|
+
conn_id: str,
|
|
14
|
+
conn_type: Literal["postgres", "mssql"],
|
|
15
|
+
**kwargs: dict,
|
|
16
|
+
) -> PythonOperator:
|
|
17
|
+
"""Create a connection if it does not exist
|
|
18
|
+
|
|
19
|
+
:param str conn_id: the connection id
|
|
20
|
+
:param Literal["postgres", "mssql"] conn_type: the connection type
|
|
21
|
+
:param dict **kwargs: additional keyword arguments
|
|
22
|
+
:return: a PythonOperator to create the connection
|
|
23
|
+
:rtype: PythonOperator
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def _create_connection():
|
|
27
|
+
"""Create a connection"""
|
|
28
|
+
session = settings.Session()
|
|
29
|
+
# Check if connection exists
|
|
30
|
+
conn = session.query(Connection).filter(Connection.conn_id == conn_id).first()
|
|
31
|
+
# If connection does not exist, create it
|
|
32
|
+
if conn is None:
|
|
33
|
+
new_conn = Connection(
|
|
34
|
+
conn_id=conn_id,
|
|
35
|
+
conn_type=conn_type,
|
|
36
|
+
host=os.getenv("PLATFORM_DB_HOST") or "atlas",
|
|
37
|
+
# FIXME - this envar needs added to the instance container app
|
|
38
|
+
schema=os.getenv("PLATFORM_DB_NAME") or "airflowtest-sqlDatabase",
|
|
39
|
+
login=os.getenv("PLATFORM_DB_USER"),
|
|
40
|
+
password=os.getenv("PLATFORM_DB_PASSWORD"),
|
|
41
|
+
port=int(
|
|
42
|
+
os.getenv(
|
|
43
|
+
"PLATFORM_DB_PORT",
|
|
44
|
+
)
|
|
45
|
+
or (5432 if conn_type == "postgres" else 1433)
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
session.add(new_conn)
|
|
49
|
+
session.commit()
|
|
50
|
+
print("Connection created.")
|
|
51
|
+
else:
|
|
52
|
+
print("Connection already exists.")
|
|
53
|
+
|
|
54
|
+
return PythonOperator(
|
|
55
|
+
task_id=f"create-connection-{conn_id}",
|
|
56
|
+
python_callable=_create_connection,
|
|
57
|
+
**kwargs,
|
|
58
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Provide workflow factory functions."""
|
|
2
|
+
|
|
3
|
+
from typing import Union
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
|
|
6
|
+
from airflow import DAG
|
|
7
|
+
from airflow.operators.python import PythonOperator
|
|
8
|
+
from airflow.utils.task_group import TaskGroup
|
|
9
|
+
|
|
10
|
+
from regscale.airflow.sensors.sql import build_sql_sensor_xcon
|
|
11
|
+
from regscale.airflow.sessions.sql.sql_server_queries import (
|
|
12
|
+
CHECK_IF_COMPLETED_SQL_QUERY,
|
|
13
|
+
)
|
|
14
|
+
from regscale.airflow.tasks.branches import tri_branch_func
|
|
15
|
+
from regscale.airflow.tasks.workflows import (
|
|
16
|
+
build_complete,
|
|
17
|
+
build_rejected,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def workflow_listener_factory(
|
|
22
|
+
workflow_name: str,
|
|
23
|
+
dag: DAG,
|
|
24
|
+
next_step_name: str,
|
|
25
|
+
next_step: Union[TaskGroup, PythonOperator],
|
|
26
|
+
unique: bool = False,
|
|
27
|
+
**kwargs,
|
|
28
|
+
) -> TaskGroup:
|
|
29
|
+
"""Build a workflow listener task group
|
|
30
|
+
|
|
31
|
+
:param str workflow_name: the name of the workflow
|
|
32
|
+
:param DAG dag: the DAG to add the task group to
|
|
33
|
+
:param str next_step_name: the name of the next step
|
|
34
|
+
:param Union[TaskGroup, PythonOperator] next_step: the next step
|
|
35
|
+
:param dict kwargs: keyword arguments to pass to the task group
|
|
36
|
+
:param bool unique: whether to make the task group unique
|
|
37
|
+
:return: a workflow listener task group
|
|
38
|
+
:rtype: TaskGroup
|
|
39
|
+
"""
|
|
40
|
+
uid = None
|
|
41
|
+
if unique:
|
|
42
|
+
uid = str(uuid4())[:8]
|
|
43
|
+
name = f"{workflow_name}-{uid}"
|
|
44
|
+
else:
|
|
45
|
+
name = f"{workflow_name}"
|
|
46
|
+
with TaskGroup(
|
|
47
|
+
group_id=name,
|
|
48
|
+
dag=dag,
|
|
49
|
+
**kwargs,
|
|
50
|
+
) as task_group:
|
|
51
|
+
# You need to have this factory upstream from the is_completed_listener_name
|
|
52
|
+
is_completed_listener_name = f"{name}-listener"
|
|
53
|
+
complete_name = f"{name}-complete"
|
|
54
|
+
# this task needs to be the name of the next listener
|
|
55
|
+
rejected_name = f"{name}-rejected"
|
|
56
|
+
is_completed = build_sql_sensor_xcon(
|
|
57
|
+
sql=CHECK_IF_COMPLETED_SQL_QUERY,
|
|
58
|
+
step_name=next_step_name,
|
|
59
|
+
name=is_completed_listener_name,
|
|
60
|
+
dag=dag,
|
|
61
|
+
)
|
|
62
|
+
complete = build_complete(uid=uid or complete_name, dag=dag)
|
|
63
|
+
rejected = build_rejected(uid=uid or rejected_name, dag=dag)
|
|
64
|
+
decision = PythonOperator(
|
|
65
|
+
task_id=f"decision-{uid or name}",
|
|
66
|
+
python_callable=tri_branch_func,
|
|
67
|
+
op_kwargs={
|
|
68
|
+
"pull_from": is_completed_listener_name,
|
|
69
|
+
"negative_task": rejected_name,
|
|
70
|
+
"neutral_task": next_step, # neutral_step_name in DAG
|
|
71
|
+
"positive_task": complete_name,
|
|
72
|
+
},
|
|
73
|
+
dag=dag,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
is_completed >> decision >> [complete, next_step, rejected]
|
|
77
|
+
|
|
78
|
+
return task_group
|