stoobly-agent 1.2.3__py3-none-any.whl → 1.4.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.
Files changed (102) hide show
  1. stoobly_agent/__init__.py +1 -1
  2. stoobly_agent/app/api/application_http_request_handler.py +3 -3
  3. stoobly_agent/app/api/proxy_controller.py +8 -7
  4. stoobly_agent/app/cli/config_cli.py +1 -1
  5. stoobly_agent/app/cli/helpers/certificate_authority.py +7 -6
  6. stoobly_agent/app/cli/helpers/print_service.py +17 -0
  7. stoobly_agent/app/cli/scaffold/app.py +16 -34
  8. stoobly_agent/app/cli/scaffold/app_command.py +4 -7
  9. stoobly_agent/app/cli/scaffold/app_config.py +15 -2
  10. stoobly_agent/app/cli/scaffold/app_create_command.py +18 -2
  11. stoobly_agent/app/cli/scaffold/command.py +1 -1
  12. stoobly_agent/app/cli/scaffold/constants.py +9 -5
  13. stoobly_agent/app/cli/scaffold/docker/app_builder.py +3 -7
  14. stoobly_agent/app/cli/scaffold/docker/constants.py +0 -1
  15. stoobly_agent/app/cli/scaffold/docker/service/builder.py +12 -11
  16. stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +14 -31
  17. stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py +6 -2
  18. stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py +6 -2
  19. stoobly_agent/app/cli/scaffold/hosts_file_manager.py +112 -0
  20. stoobly_agent/app/cli/scaffold/service.py +1 -2
  21. stoobly_agent/app/cli/scaffold/service_command.py +1 -1
  22. stoobly_agent/app/cli/scaffold/service_config.py +10 -14
  23. stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py +9 -11
  24. stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context +2 -4
  25. stoobly_agent/app/cli/scaffold/templates/app/.Makefile +108 -68
  26. stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml +8 -13
  27. stoobly_agent/app/cli/scaffold/templates/app/Makefile +1 -1
  28. stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml +8 -4
  29. stoobly_agent/app/cli/scaffold/templates/app/build/mock/.docker-compose.mock.yml +2 -6
  30. stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/.configure +3 -0
  31. stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/.init +3 -0
  32. stoobly_agent/app/cli/scaffold/templates/app/build/record/.docker-compose.record.yml +2 -6
  33. stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/.configure +3 -0
  34. stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/.init +3 -0
  35. stoobly_agent/app/cli/scaffold/templates/app/build/test/.docker-compose.test.yml +2 -6
  36. stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/.configure +3 -0
  37. stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/.init +3 -0
  38. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml +2 -0
  39. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/.docker-compose.mock.yml +2 -8
  40. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/.docker-compose.record.yml +2 -8
  41. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/.docker-compose.test.yml +2 -8
  42. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/.docker-compose.exec.yml +2 -3
  43. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.logs +1 -0
  44. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.services +9 -0
  45. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.mock.yml +1 -2
  46. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.record.yml +1 -2
  47. stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/.configure +3 -0
  48. stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/.init +7 -1
  49. stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/.configure +3 -0
  50. stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/.init +7 -1
  51. stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/.configure +3 -0
  52. stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/.init +7 -1
  53. stoobly_agent/app/cli/scaffold/validate_command.py +2 -2
  54. stoobly_agent/app/cli/scaffold/workflow.py +5 -4
  55. stoobly_agent/app/cli/scaffold/workflow_command.py +3 -3
  56. stoobly_agent/app/cli/scaffold/workflow_create_command.py +0 -1
  57. stoobly_agent/app/cli/scaffold/workflow_run_command.py +78 -45
  58. stoobly_agent/app/cli/scaffold_cli.py +246 -109
  59. stoobly_agent/app/cli/snapshot_cli.py +7 -3
  60. stoobly_agent/app/models/adapters/joined_request_adapter.py +6 -0
  61. stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py +3 -1
  62. stoobly_agent/app/models/helpers/apply.py +34 -17
  63. stoobly_agent/app/models/helpers/create_request_params_service.py +4 -0
  64. stoobly_agent/app/proxy/handle_mock_service.py +2 -0
  65. stoobly_agent/app/proxy/handle_replay_service.py +2 -0
  66. stoobly_agent/app/proxy/mitmproxy/request_facade.py +1 -1
  67. stoobly_agent/app/proxy/mitmproxy/response_body_facade.py +19 -0
  68. stoobly_agent/app/proxy/mitmproxy/response_facade.py +90 -18
  69. stoobly_agent/app/proxy/record/join_request_service.py +1 -1
  70. stoobly_agent/app/proxy/replay/body_parser_service.py +11 -3
  71. stoobly_agent/app/settings/constants/request_component.py +2 -1
  72. stoobly_agent/config/constants/custom_headers.py +13 -13
  73. stoobly_agent/config/constants/headers.py +0 -2
  74. stoobly_agent/config/data_dir.py +2 -1
  75. stoobly_agent/config/schema.yml +2 -2
  76. stoobly_agent/public/18-es2015.583f191cc7ad512ee262.js +1 -0
  77. stoobly_agent/public/18-es5.583f191cc7ad512ee262.js +1 -0
  78. stoobly_agent/public/35-es2015.8f79ff8748d4ff06ab03.js +1 -0
  79. stoobly_agent/public/35-es5.8f79ff8748d4ff06ab03.js +1 -0
  80. stoobly_agent/public/index.html +1 -1
  81. stoobly_agent/public/main-es2015.2cc16523aa3fcaba51e5.js +1 -0
  82. stoobly_agent/public/main-es5.2cc16523aa3fcaba51e5.js +1 -0
  83. stoobly_agent/public/{runtime-es2015.9addf49b79aca951b7e2.js → runtime-es2015.b914470164e4d6e75d96.js} +1 -1
  84. stoobly_agent/public/{runtime-es5.9addf49b79aca951b7e2.js → runtime-es5.b914470164e4d6e75d96.js} +1 -1
  85. stoobly_agent/test/app/cli/scaffold/cli_invoker.py +1 -2
  86. stoobly_agent/test/app/cli/scaffold/{hosts_file_reader_test.py → hosts_file_manager_test.py} +20 -20
  87. stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py +162 -1
  88. stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
  89. stoobly_agent/test/mock_data/scaffold/docker-compose-assets-service.yml +1 -3
  90. {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/METADATA +1 -1
  91. {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/RECORD +94 -93
  92. stoobly_agent/app/cli/scaffold/hosts_file_reader.py +0 -65
  93. stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.proxy +0 -34
  94. stoobly_agent/public/18-es2015.d3b430636a4d6f544d92.js +0 -1
  95. stoobly_agent/public/18-es5.d3b430636a4d6f544d92.js +0 -1
  96. stoobly_agent/public/35-es2015.f741ebce0bfc25f0ec99.js +0 -1
  97. stoobly_agent/public/35-es5.f741ebce0bfc25f0ec99.js +0 -1
  98. stoobly_agent/public/main-es2015.ccd46ac1b6638ddf2066.js +0 -1
  99. stoobly_agent/public/main-es5.ccd46ac1b6638ddf2066.js +0 -1
  100. {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/LICENSE +0 -0
  101. {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/WHEEL +0 -0
  102. {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,8 @@
1
1
  #!/bin/bash
2
2
 
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
3
6
  stoobly-agent config reset
4
7
 
5
8
  cd $(dirname -- "$0") && ./configure
@@ -1,5 +1,8 @@
1
1
  #!/bin/bash
2
2
 
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
3
6
  stoobly-agent snapshot apply
4
7
 
5
8
  cd $(dirname -- "$0") && ./init
@@ -1,22 +1,18 @@
1
1
  services:
2
2
  build.init:
3
- build:
4
- context: ./
5
- dockerfile: ../../.Dockerfile.context
6
3
  extends:
7
4
  file: ../.docker-compose.base.yml
8
5
  service: build.init_base
6
+ image: stoobly.${USER_ID}
9
7
  profiles:
10
8
  - test
11
9
  build.configure:
12
- build:
13
- context: ./
14
- dockerfile: ../../.Dockerfile.context
15
10
  depends_on:
16
11
  build.init:
17
12
  condition: service_completed_successfully
18
13
  extends:
19
14
  file: ../.docker-compose.base.yml
20
15
  service: build.configure_base
16
+ image: stoobly.${USER_ID}
21
17
  profiles:
22
18
  - test
@@ -1,5 +1,8 @@
1
1
  #!/bin/bash
2
2
 
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
3
6
  stoobly-agent config reset
4
7
 
5
8
  cd $(dirname -- "$0") && ./configure
@@ -1,5 +1,8 @@
1
1
  #!/bin/bash
2
2
 
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
3
6
  stoobly-agent snapshot apply
4
7
 
5
8
  cd $(dirname -- "$0") && ./init
@@ -7,6 +7,7 @@ services:
7
7
  extends:
8
8
  file: ../.docker-compose.base.yml
9
9
  service: context_base
10
+ working_dir: /home/stoobly/.stoobly/docker/entrypoint/${WORKFLOW_NAME}
10
11
  entrypoint.init_base:
11
12
  command:
12
13
  - bin/.init
@@ -14,3 +15,4 @@ services:
14
15
  extends:
15
16
  file: ../.docker-compose.base.yml
16
17
  service: context_base
18
+ working_dir: /home/stoobly/.stoobly/docker/entrypoint/${WORKFLOW_NAME}
@@ -3,9 +3,6 @@ networks:
3
3
  name: entrypoint
4
4
  services:
5
5
  entrypoint.configure:
6
- build:
7
- context: ./
8
- dockerfile: ../../.Dockerfile.context
9
6
  depends_on:
10
7
  entrypoint.init:
11
8
  condition: service_completed_successfully
@@ -13,19 +10,16 @@ services:
13
10
  extends:
14
11
  file: ../.docker-compose.base.yml
15
12
  service: entrypoint.configure_base
13
+ image: stoobly.${USER_ID}
16
14
  profiles:
17
15
  - mock
18
16
  volumes: []
19
17
  entrypoint.init:
20
- build:
21
- context: ./
22
- dockerfile: ../../.Dockerfile.context
23
18
  environment: {}
24
19
  extends:
25
20
  file: ../.docker-compose.base.yml
26
21
  service: entrypoint.init_base
22
+ image: stoobly.${USER_ID}
27
23
  profiles:
28
24
  - mock
29
- volumes:
30
- - ./dist:/home/stoobly/dist
31
25
  volumes: {}
@@ -3,9 +3,6 @@ networks:
3
3
  name: entrypoint
4
4
  services:
5
5
  entrypoint.configure:
6
- build:
7
- context: ./
8
- dockerfile: ../../.Dockerfile.context
9
6
  depends_on:
10
7
  entrypoint.init:
11
8
  condition: service_completed_successfully
@@ -13,19 +10,16 @@ services:
13
10
  extends:
14
11
  file: ../.docker-compose.base.yml
15
12
  service: entrypoint.configure_base
13
+ image: stoobly.${USER_ID}
16
14
  profiles:
17
15
  - record
18
16
  volumes: []
19
17
  entrypoint.init:
20
- build:
21
- context: ./
22
- dockerfile: ../../.Dockerfile.context
23
18
  environment: {}
24
19
  extends:
25
20
  file: ../.docker-compose.base.yml
26
21
  service: entrypoint.init_base
22
+ image: stoobly.${USER_ID}
27
23
  profiles:
28
24
  - record
29
- volumes:
30
- - ./dist:/home/stoobly/dist
31
25
  volumes: {}
@@ -3,9 +3,6 @@ networks:
3
3
  name: entrypoint
4
4
  services:
5
5
  entrypoint.configure:
6
- build:
7
- context: ./
8
- dockerfile: ../../.Dockerfile.context
9
6
  depends_on:
10
7
  entrypoint.init:
11
8
  condition: service_completed_successfully
@@ -13,19 +10,16 @@ services:
13
10
  extends:
14
11
  file: ../.docker-compose.base.yml
15
12
  service: entrypoint.configure_base
13
+ image: stoobly.${USER_ID}
16
14
  profiles:
17
15
  - test
18
16
  volumes: []
19
17
  entrypoint.init:
20
- build:
21
- context: ./
22
- dockerfile: ../../.Dockerfile.context
23
18
  environment: {}
24
19
  extends:
25
20
  file: ../.docker-compose.base.yml
26
21
  service: entrypoint.init_base
22
+ image: stoobly.${USER_ID}
27
23
  profiles:
28
24
  - test
29
- volumes:
30
- - ./dist:/home/stoobly/dist
31
25
  volumes: {}
@@ -1,12 +1,11 @@
1
1
  services:
2
2
  stoobly_ui.command:
3
- build:
4
- dockerfile: ../../.Dockerfile.context
5
- command: ['${EXEC_COMMAND}', '${EXEC_ARGS}']
3
+ command: ['.stoobly/docker/stoobly-ui/exec/${EXEC_COMMAND}', '${EXEC_ARGS}']
6
4
  environment:
7
5
  EXEC_OPTIONS: ${EXEC_OPTIONS}
8
6
  extends:
9
7
  file: ../.docker-compose.base.yml
10
8
  service: stoobly_ui.base
9
+ image: stoobly.${USER_ID}
11
10
  profiles:
12
11
  - exec
@@ -6,5 +6,6 @@ workflow=$1
6
6
  stoobly-agent scaffold workflow logs \
7
7
  --app-dir-path "$(pwd)" \
8
8
  --dry-run \
9
+ --log-level warning \
9
10
  $extra_options \
10
11
  $workflow > .stoobly/tmp/run.sh
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+
3
+ extra_options=$EXEC_OPTIONS
4
+ workflow=$1
5
+
6
+ stoobly-agent scaffold service list \
7
+ --app-dir-path "$(pwd)" \
8
+ --workflow $workflow \
9
+ $extra_options
@@ -1,11 +1,10 @@
1
1
  services:
2
2
  stoobly_ui.service:
3
- build:
4
- dockerfile: ../../.Dockerfile.context
5
3
  command: --ui-port 4200
6
4
  extends:
7
5
  file: ../.docker-compose.base.yml
8
6
  service: stoobly_ui.base
7
+ image: stoobly.${USER_ID}
9
8
  ports:
10
9
  - '4200:4200'
11
10
  profiles:
@@ -1,11 +1,10 @@
1
1
  services:
2
2
  stoobly_ui.service:
3
- build:
4
- dockerfile: ../../.Dockerfile.context
5
3
  command: --ui-port 4200
6
4
  extends:
7
5
  file: ../.docker-compose.base.yml
8
6
  service: stoobly_ui.base
7
+ image: stoobly.${USER_ID}
9
8
  ports:
10
9
  - '4200:4200'
11
10
  profiles:
@@ -1,5 +1,8 @@
1
1
  #!/bin/bash
2
2
 
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
3
6
  entrypoint=$(dirname -- "$0")/configure
4
7
 
5
8
  if [ -e "$entrypoint" ]; then
@@ -1,7 +1,13 @@
1
1
  #!/bin/bash
2
2
 
3
- entrypoint=$(dirname -- "$0")/init
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
6
+ if [ -n "$SERVICE_HOSTNAME" ] && [ "$SERVICE_SCHEME" = "https" ]; then
7
+ stoobly-agent ca-cert mkcert $SERVICE_HOSTNAME
8
+ fi
4
9
 
10
+ entrypoint=$(dirname -- "$0")/init
5
11
  if [ -e "$entrypoint" ]; then
6
12
  "$entrypoint"
7
13
  fi
@@ -1,5 +1,8 @@
1
1
  #!/bin/bash
2
2
 
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
3
6
  entrypoint=$(dirname -- "$0")/configure
4
7
 
5
8
  if [ -e "$entrypoint" ]; then
@@ -1,7 +1,13 @@
1
1
  #!/bin/bash
2
2
 
3
- entrypoint=$(dirname -- "$0")/init
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
6
+ if [ -n "$SERVICE_HOSTNAME" ] && [ "$SERVICE_SCHEME" = "https" ] ; then
7
+ stoobly-agent ca-cert mkcert $SERVICE_HOSTNAME
8
+ fi
4
9
 
10
+ entrypoint=$(dirname -- "$0")/init
5
11
  if [ -e "$entrypoint" ]; then
6
12
  "$entrypoint"
7
13
  fi
@@ -1,5 +1,8 @@
1
1
  #!/bin/bash
2
2
 
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
3
6
  entrypoint=$(dirname -- "$0")/configure
4
7
 
5
8
  if [ -e "$entrypoint" ]; then
@@ -1,7 +1,13 @@
1
1
  #!/bin/bash
2
2
 
3
- entrypoint=$(dirname -- "$0")/init
3
+ # This file was automatically generated. DO NOT EDIT.
4
+ # Any changes made to this file will be overwritten.
5
+
6
+ if [ -n "$SERVICE_HOSTNAME" ] && [ "$SERVICE_SCHEME" = "https" ]; then
7
+ stoobly-agent ca-cert mkcert $SERVICE_HOSTNAME
8
+ fi
4
9
 
10
+ entrypoint=$(dirname -- "$0")/init
5
11
  if [ -e "$entrypoint" ]; then
6
12
  "$entrypoint"
7
13
  fi
@@ -35,7 +35,7 @@ class ValidateCommand():
35
35
  if logs and re.search('error', str(logs), re.IGNORECASE):
36
36
  raise ScaffoldValidateException(f"Error logs potentially detected in: {init_container_name}")
37
37
  if init_container.status != 'exited' or init_container.attrs['State']['ExitCode'] != 0:
38
- raise ScaffoldValidateException(f"init container has not exited like expected: {init_container_name}")
38
+ raise ScaffoldValidateException(f"init container {init_container_name} exited with: {init_container.attrs['State']['ExitCode']}")
39
39
 
40
40
  configure_container = self.__get_container(configure_container_name)
41
41
 
@@ -43,7 +43,7 @@ class ValidateCommand():
43
43
  if configure_container.status == 'exited' and configure_container.attrs['State']['ExitCode'] == 0:
44
44
  configure_container_ran = True
45
45
  if not configure_container_ran:
46
- raise ScaffoldValidateException(f"Configure container has not ran as expected: {configure_container_name}")
46
+ raise ScaffoldValidateException(f"configure container {configure_container_name} exited with: {configure_container.attrs['State']['ExitCode']}")
47
47
 
48
48
  def validate_detached(self, container: Container) -> None:
49
49
  print(f"Validating detached for: {container.name}")
@@ -33,14 +33,15 @@ class Workflow():
33
33
 
34
34
  @property
35
35
  def service_paths(self):
36
- return self.app.service_paths
36
+ all_service_paths = self.app.service_paths
37
+ return list(filter(lambda path: os.path.exists(os.path.join(path, self.workflow_name)), all_service_paths))
37
38
 
38
39
  # TODO: merge into 1 services property
39
40
 
40
41
  # Returns services that run in this specific workflow
41
42
  @property
42
43
  def services_ran(self) -> List[str]:
43
- services_dir = os.path.join(self.app.scaffold_dir_path, self.app.namespace)
44
+ services_dir = os.path.join(self.app.data_dir_path, self.app.scaffold_namespace)
44
45
 
45
46
  services = []
46
47
  for filename in os.listdir(services_dir):
@@ -56,5 +57,5 @@ class Workflow():
56
57
  return services
57
58
 
58
59
  def service_paths_from_services(self, services: List[str]):
59
- app_namespace_path = self.app.namespace_path
60
- return list(map(lambda service: os.path.join(app_namespace_path, service), services))
60
+ scaffold_namespace_path = self.app.scaffold_namespace_path
61
+ return list(map(lambda service: os.path.join(scaffold_namespace_path, service), services))
@@ -26,7 +26,7 @@ class WorkflowCommand(ServiceCommand):
26
26
  @property
27
27
  def compose_path(self):
28
28
  return os.path.join(
29
- self.scaffold_dir_path,
29
+ self.data_dir_path,
30
30
  self.compose_relative_path
31
31
  )
32
32
 
@@ -70,7 +70,7 @@ class WorkflowCommand(ServiceCommand):
70
70
  @property
71
71
  def custom_compose_path(self):
72
72
  return os.path.join(
73
- self.scaffold_dir_path,
73
+ self.data_dir_path,
74
74
  self.custom_compose_relative_path
75
75
  )
76
76
 
@@ -114,7 +114,7 @@ class WorkflowCommand(ServiceCommand):
114
114
  @property
115
115
  def workflow_path(self):
116
116
  return os.path.join(
117
- self.scaffold_dir_path,
117
+ self.data_dir_path,
118
118
  self.workflow_relative_path
119
119
  )
120
120
 
@@ -17,7 +17,6 @@ CORE_WORKFLOWS = [WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE, WORKFLOW_TEST_TYPE]
17
17
 
18
18
  class BuildOptions(TypedDict):
19
19
  builder_class: type
20
- headless: bool
21
20
  service_builder: ServiceBuilder
22
21
  template: WORKFLOW_TEMPLATE
23
22
  workflow_decorators: List[Union[MockDecorator, ReverseProxyDecorator]]
@@ -12,42 +12,47 @@ from stoobly_agent.lib.logger import Logger
12
12
  from .app import App
13
13
  from .constants import (
14
14
  APP_NETWORK_ENV, CA_CERTS_DIR_ENV, CERTS_DIR_ENV, CONTEXT_DIR_ENV, NAMESERVERS_FILE,
15
- SERVICE_DNS_ENV, SERVICE_NAME_ENV, USER_ID_ENV, WORKFLOW_NAME_ENV
15
+ SERVICE_DNS_ENV, SERVICE_NAME_ENV, USER_ID_ENV, WORKFLOW_NAME_ENV, WORKFLOW_NAMESPACE_ENV
16
16
  )
17
+ from .docker.constants import DOCKERFILE_CONTEXT
17
18
  from .workflow_command import WorkflowCommand
18
19
  from .workflow_env import WorkflowEnv
19
20
 
20
21
  LOG_ID = 'WorkflowRunCommand'
21
22
 
22
- class UpOptions(TypedDict):
23
- attached: bool
23
+ class ComposeOptions(TypedDict):
24
24
  namespace: str
25
- no_cache: bool
25
+ user_id: str
26
+
27
+ class BuildOptions(ComposeOptions):
28
+ user_id: str
26
29
  verbose: bool
27
30
 
31
+ class DownOptions(ComposeOptions):
32
+ extra_compose_path: str
33
+ rmi: bool
34
+
35
+ class UpOptions(ComposeOptions):
36
+ attached: bool
37
+ extra_compose_path: str
38
+ pull: bool
39
+
28
40
  class WorkflowRunCommand(WorkflowCommand):
29
41
  def __init__(self, app: App, **kwargs):
30
42
  super().__init__(app, **kwargs)
31
43
 
32
44
  self.__current_working_dir = os.getcwd()
33
- self.__ca_certs_dir_path = app.ca_certs_dir_path
34
- self.__certs_dir_path = app.certs_dir_path
35
- self.__context_dir_path = app.context_dir_path
36
- self.__extra_compose_path = kwargs.get('extra_compose_path')
45
+ self.__ca_certs_dir_path = kwargs.get('ca_certs_dir_path') or app.ca_certs_dir_path
46
+ self.__certs_dir_path = kwargs.get('certs_dir_path') or app.certs_dir_path
47
+ self.__context_dir_path = kwargs.get('context_dir_path') or app.context_dir_path
37
48
  self.__network = kwargs.get('network') or self.app_config.network
38
49
 
39
50
  @property
40
51
  def ca_certs_dir_path(self):
41
- if not os.path.exists(self.__ca_certs_dir_path):
42
- os.makedirs(self.__ca_certs_dir_path)
43
-
44
52
  return self.__ca_certs_dir_path
45
53
 
46
54
  @property
47
55
  def certs_dir_path(self):
48
- if not os.path.exists(self.__certs_dir_path):
49
- os.makedirs(self.__certs_dir_path)
50
-
51
56
  return self.__certs_dir_path
52
57
 
53
58
  @property
@@ -66,10 +71,6 @@ class WorkflowRunCommand(WorkflowCommand):
66
71
  def current_working_dir(self, v):
67
72
  self.__current_working_dir = v
68
73
 
69
- @property
70
- def extra_compose_path(self):
71
- return self.__extra_compose_path
72
-
73
74
  @property
74
75
  def nameservers(self):
75
76
  path = self.nameservers_path
@@ -88,17 +89,43 @@ class WorkflowRunCommand(WorkflowCommand):
88
89
  def network(self):
89
90
  return self.__network
90
91
 
92
+ def create_image(self, **options: BuildOptions):
93
+ relative_namespace_path = os.path.relpath(self.scaffold_namespace_path, self.__current_working_dir)
94
+ dockerfile_path = os.path.join(relative_namespace_path, DOCKERFILE_CONTEXT)
95
+ user_id = options['user_id'] or os.getuid()
96
+
97
+ command = ['docker', 'build']
98
+ command.append(f"-f {dockerfile_path}")
99
+ command.append(f"-t stoobly.{user_id}")
100
+ command.append(f"--build-arg USER_ID={user_id}")
101
+
102
+ if not os.environ.get('STOOBLY_IMAGE_USE_LOCAL'):
103
+ command.append('--pull')
104
+
105
+ if not options.get('verbose'):
106
+ command.append('--quiet')
107
+
108
+ # To avoid large context transfer times, should be a folder with relatively low number of files
109
+ command.append(relative_namespace_path)
110
+
111
+ return ' '.join(command)
112
+
113
+ def remove_image(self, user_id: str = None):
114
+ user_id = user_id or os.getuid()
115
+ command = ['docker', 'rmi', f"stoobly.{user_id}", '&>', '/dev/null']
116
+ command.append('|| true')
117
+ return ' '.join(command)
118
+
91
119
  def create_network(self):
92
- return f"docker network create {self.network} 2> /dev/null"
120
+ return f"docker network create {self.network} &> /dev/null"
93
121
 
94
122
  def remove_network(self):
95
- return f"docker network rm {self.network} > /dev/null"
123
+ return f"docker network rm {self.network} &> /dev/null || true"
96
124
 
97
125
  def up(self, **options: UpOptions):
98
126
  if not os.path.exists(self.compose_path):
99
127
  return ''
100
128
 
101
- build_command = ['docker', 'compose']
102
129
  command = ['COMPOSE_IGNORE_ORPHANS=true', 'docker', 'compose']
103
130
  command_options = []
104
131
 
@@ -123,27 +150,24 @@ class WorkflowRunCommand(WorkflowCommand):
123
150
 
124
151
  command_options.append(f"-f {os.path.relpath(self.custom_compose_path, self.__current_working_dir)}")
125
152
 
126
- if self.extra_compose_path:
127
- command_options.append(f"-f {os.path.relpath(self.extra_compose_path, self.__current_working_dir)}")
153
+ if options.get('extra_compose_path'):
154
+ command_options.append(f"-f {os.path.relpath(options['extra_compose_path'], self.__current_working_dir)}")
128
155
 
129
156
  command_options.append(f"--profile {self.workflow_name}")
130
157
 
131
- if options.get('namespace'):
132
- command_options.append(f"-p {options['namespace']}")
133
-
134
- build_command += command_options
135
- build_command.append('build')
136
- build_command.append('--pull')
137
-
138
- if not options.get('verbose'):
139
- build_command.append('--quiet')
140
-
141
- if options.get('no_cache'):
142
- build_command.append('--no-cache')
158
+ if not options.get('namespace'):
159
+ options['namespace'] = self.workflow_name
160
+ command_options.append(f"-p {options['namespace']}")
143
161
 
144
162
  command += command_options
145
163
  command.append('up')
146
164
 
165
+ if options.get('build'):
166
+ command.append('--build')
167
+
168
+ if options.get('pull'):
169
+ command.append('--pull missing')
170
+
147
171
  if not options.get('attached'):
148
172
  command.append('-d')
149
173
  else:
@@ -159,11 +183,11 @@ class WorkflowRunCommand(WorkflowCommand):
159
183
  # Otherwise, even if a service exits with a non-zero exit code, exit code 0 is returned
160
184
  command.append(option)
161
185
 
162
- self.write_env()
186
+ self.write_env(**options)
163
187
 
164
- return ' && '.join([' '.join(build_command), ' '.join(command)])
188
+ return ' '.join(command)
165
189
 
166
- def down(self, **options):
190
+ def down(self, **options: DownOptions):
167
191
  if not os.path.exists(self.compose_path):
168
192
  return ''
169
193
 
@@ -176,18 +200,21 @@ class WorkflowRunCommand(WorkflowCommand):
176
200
  if self.custom_services:
177
201
  command.append(f"-f {os.path.relpath(self.custom_compose_path, self.__current_working_dir)}")
178
202
 
179
- if self.extra_compose_path:
180
- command.append(f"-f {os.path.relpath(self.extra_compose_path, self.__current_working_dir)}")
203
+ if options.get('extra_compose_path'):
204
+ command.append(f"-f {os.path.relpath(options['extra_compose_path'], self.__current_working_dir)}")
181
205
 
182
206
  command.append(f"--profile {self.workflow_name}")
183
207
 
184
- if options.get('namespace'):
185
- command.append(f"-p {options['namespace']}")
208
+ if not options.get('namespace'):
209
+ options['namespace'] = self.workflow_name
210
+ command.append(f"-p {options['namespace']}")
186
211
 
187
212
  command.append('down')
188
213
 
189
214
  if options.get('rmi'):
190
- command.append(f"--rmi local")
215
+ command.append('--rmi local')
216
+
217
+ self.write_env(**options)
191
218
 
192
219
  return ' '.join(command)
193
220
 
@@ -212,14 +239,20 @@ class WorkflowRunCommand(WorkflowCommand):
212
239
  if nameservers:
213
240
  fp.write("\n".join(nameservers))
214
241
 
215
- def write_env(self):
242
+ def write_env(self, **options: ComposeOptions):
243
+ namespace = options.get('namespace')
244
+ user_id = options.get('user_id')
245
+
216
246
  _config = {}
217
247
  _config[CA_CERTS_DIR_ENV] = self.ca_certs_dir_path
218
248
  _config[CERTS_DIR_ENV] = self.certs_dir_path
219
249
  _config[CONTEXT_DIR_ENV] = self.context_dir_path
220
250
  _config[SERVICE_NAME_ENV] = self.service_name
221
- _config[USER_ID_ENV] = os.getuid()
251
+ _config[USER_ID_ENV] = user_id or os.getuid()
222
252
  _config[WORKFLOW_NAME_ENV] = self.workflow_name
253
+
254
+ if namespace:
255
+ _config[WORKFLOW_NAMESPACE_ENV] = namespace
223
256
 
224
257
  if self.network:
225
258
  _config[APP_NETWORK_ENV] = self.network