6ebb38d9731794db4222ba40aca9075bc8c67dc4
[osm/devops.git] /
1 #######################################################################################
2 # Copyright ETSI Contributors and Others.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #    http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #######################################################################################
17
18 # ===========================================================================================
19 # OSM Application Management Workflow Template
20 # ===========================================================================================
21 #
22 # PREREQUISITES:
23 # - PVC containing application model and parameters must exist
24 # - Required files: app_instance_model.yaml, parameters/clear/environment.yaml, parameters/secret/environment.yaml
25 # - Git repositories for Fleet and SW-Catalogs must be accessible
26 #
27 # USAGE:
28 # Use the launcher workflows (launcher-app.yaml) or create custom workflows that reference
29 # this template. Ensure the model_volume_name parameter points to a properly prepared PVC.
30 #
31 # For volume preparation utilities, see: scripts/library/osm/utils/workflow-pvc
32 #
33 # ===========================================================================================
34
35 apiVersion: argoproj.io/v1alpha1
36 kind: WorkflowTemplate
37 metadata:
38   name: full-app-management-wft
39   namespace: osm-workflows
40 spec:
41   arguments:
42     parameters:
43     # === Core Operation Configuration ===
44     - name: command
45       description: |
46         Full command string to execute with the OSM SDK.
47         Examples:
48         - "app create $environment": Deploy new application instance
49         - "app update $environment": Update existing application instance
50         - "app delete $environment": Remove application instance
51         This parameter accepts any valid OSM SDK command for maximum flexibility.
52
53     # === Volume-Based Data Sources ===
54     - name: model_volume_name
55       description: |
56         Name of the PersistentVolumeClaim containing application models and parameters.
57         The PVC must contain the following structure:
58         - /app_instance_model.yaml: Application instantiation model (required)
59         - /parameters/clear/environment.yaml: Clear text parameters (optional)
60         Secret parameters are mounted separately via the secret_name parameter.
61     - name: secret_name
62       description: |
63         Name of the Kubernetes secret to mount at /model/parameters/secret/environment.yaml.
64         This parameter is optional. When provided, the secret will be mounted as a file
65         containing sensitive configuration parameters for the application.
66
67     # === Git Repository Configuration ===
68     - name: git_fleet_url
69     - name: fleet_destination_folder
70       value: "/fleet/fleet-osm"
71     - name: git_fleet_cred_secret
72     - name: git_sw_catalogs_url
73     - name: sw_catalogs_destination_folder
74       value: "/sw-catalogs/sw-catalogs-osm"
75     - name: git_sw_catalogs_cred_secret
76
77     # === Target Deployment Configuration ===
78     - name: app_name
79     - name: profile_name
80     - name: profile_type
81       value: "app-profiles"
82     - name: project_name
83       value: "osm_admin"
84
85     # === OSM SDK Container Configuration ===
86     - name: osm_sdk_image_repository
87       value: "opensourcemano/osm-nushell-krm-functions"
88       description: |
89         Repository for the OSM SDK container image. Default is the standardized
90         OSM SDK operations image that provides consistent behavior across workflows.
91     - name: osm_sdk_image_tag
92       value: "testing-daily"
93       description: |
94         Tag for the OSM SDK container image. Default is '24h' which provides
95         a stable, time-limited image for workflow execution.
96
97     # === Workflow Execution Control ===
98     - name: debug
99       value: "false"
100       description: |
101         Enable debug mode for detailed workflow execution information.
102         When set to 'true', additional debug steps will execute to provide:
103         - Volume mount status and accessibility checks
104         - Model content analysis and validation
105         - Repository structure inspection
106         - Embedded operations testing
107         Set to 'true' for troubleshooting workflow issues.
108     - name: dry_run
109       value: "false"
110       description: |
111         Enable dry-run mode to validate operations without making changes.
112         When set to 'true', the workflow will:
113         - Perform all validation and preparation steps
114         - Execute app operations in dry-run mode (no actual changes)
115         - Skip Git commit and push operations
116         Use this mode to test workflow configuration and validate models.
117
118   # === Workflow Lifecycle Management ===
119   # TTL strategy for automatic cleanup of completed workflow instances
120   ttlStrategy:
121     secondsAfterCompletion: 6000  # 100 minutes - Time to live after workflow completion
122     secondsAfterSuccess: 6000     # 100 minutes - Time to live after successful execution
123     secondsAfterFailure: 9000     # 150 minutes - Extended time for failure analysis
124
125   # Main entry point for the workflow template
126   entrypoint: app-management
127
128   templates:
129   # === Main Application Management Template ===
130   # This template orchestrates the complete application lifecycle management process
131   # using a volume-based approach for application models and parameters.
132   - name: app-management
133     inputs:
134       parameters:
135       # Core operation configuration
136       - name: command
137       # Volume-based data sources
138       - name: model_volume_name
139       - name: secret_name
140       # Git repository configuration for Fleet and SW-Catalogs
141       - name: git_fleet_url
142       - name: fleet_destination_folder
143       - name: git_fleet_cred_secret
144       - name: git_sw_catalogs_url
145       - name: sw_catalogs_destination_folder
146       - name: git_sw_catalogs_cred_secret
147       # Target deployment configuration
148       - name: app_name
149       - name: profile_name
150       - name: profile_type
151       - name: project_name
152       # OSM SDK container configuration with standardized defaults
153       - name: osm_sdk_image_repository
154       - name: osm_sdk_image_tag
155       # Workflow execution control flags
156       - name: debug
157       - name: dry_run
158
159     steps:
160     # === PHASE 1: Parameter Validation ===
161     # Validate that all required parameters are provided for volume-based workflow execution
162     - - name: validate-parameters
163         inline:
164           container:
165             image: alpine:latest
166             command: [sh, -c]
167             args:
168               - |
169                 set -e
170                 echo "=== Volume-Based Workflow Parameter Validation ==="
171                 echo "Validating required parameters for application management..."
172
173                 # Validate core volume parameter
174                 if [ -z "{{inputs.parameters.model_volume_name}}" ]; then
175                   echo "ERROR: model_volume_name parameter is required for volume-based workflow"
176                   echo "Please provide a PVC name containing the application model and parameters"
177                   exit 1
178                 fi
179
180                 # Validate Git repository parameters
181                 if [ -z "{{inputs.parameters.git_fleet_url}}" ]; then
182                   echo "ERROR: git_fleet_url parameter is required for Fleet repository access"
183                   exit 1
184                 fi
185
186                 if [ -z "{{inputs.parameters.git_sw_catalogs_url}}" ]; then
187                   echo "ERROR: git_sw_catalogs_url parameter is required for SW-Catalogs repository access"
188                   exit 1
189                 fi
190
191                 # Validate target deployment parameters
192                 if [ -z "{{inputs.parameters.app_name}}" ]; then
193                   echo "ERROR: app_name parameter is required for application identification"
194                   exit 1
195                 fi
196
197                 if [ -z "{{inputs.parameters.profile_name}}" ]; then
198                   echo "ERROR: profile_name parameter is required for deployment profile"
199                   exit 1
200                 fi
201
202                 echo "✓ All required parameters validated successfully"
203                 echo "Configuration Summary:"
204                 echo "  Command: {{inputs.parameters.command}}"
205                 echo "  Model Volume: {{inputs.parameters.model_volume_name}}"
206                 echo "  Secret Name: {{inputs.parameters.secret_name}}"
207                 echo "  Application: {{inputs.parameters.app_name}}"
208                 echo "  Profile: {{inputs.parameters.profile_name}} ({{inputs.parameters.profile_type}})"
209                 echo "  Project: {{inputs.parameters.project_name}}"
210                 echo "  OSM SDK: {{inputs.parameters.osm_sdk_image_repository}}:{{inputs.parameters.osm_sdk_image_tag}}"
211                 echo "  Debug Mode: {{inputs.parameters.debug}}"
212                 echo "  Dry Run: {{inputs.parameters.dry_run}}"
213
214     # === PHASE 2: Volume Preparation ===
215     # Generate temporary volumes for Git repository operations
216     # These volumes will store cloned Fleet and SW-Catalogs repositories during workflow execution
217     - - name: generate-fleet-volume-repo
218         templateRef:
219           name: k8s-resources-wft
220           template: generate-volume
221         arguments:
222           parameters:
223             - name: pvc-size
224               value: '100Mi'  # Sufficient space for Fleet repository content
225       - name: generate-sw-catalogs-volume-repo
226         templateRef:
227           name: k8s-resources-wft
228           template: generate-volume
229         arguments:
230           parameters:
231             - name: pvc-size
232               value: '100Mi'  # Sufficient space for SW-Catalogs repository content
233
234     # === PHASE 3: Git Repository Cloning ===
235     # Clone the required Git repositories for application modeling and deployment
236     # These repositories provide the templates and target locations for GitOps operations
237     - - name: clone-fleet
238         templateRef:
239           name: git-wft
240           template: git-clone
241         arguments:
242           parameters:
243           - name: mount_path
244             value: "/fleet"
245           - name: repo_url
246             value: "{{inputs.parameters.git_fleet_url}}"
247           - name: destination_folder
248             value: "{{inputs.parameters.fleet_destination_folder}}"
249           - name: git_cred_secret
250             value: "{{inputs.parameters.git_fleet_cred_secret}}"
251           - name: git_volume_name
252             value: '{{steps.generate-fleet-volume-repo.outputs.parameters.pvc-name}}'
253       - name: clone-sw-catalogs
254         templateRef:
255           name: git-wft
256           template: git-clone
257         arguments:
258           parameters:
259           - name: mount_path
260             value: "/sw-catalogs"
261           - name: repo_url
262             value: "{{inputs.parameters.git_sw_catalogs_url}}"
263           - name: destination_folder
264             value: "{{inputs.parameters.sw_catalogs_destination_folder}}"
265           - name: git_cred_secret
266             value: "{{inputs.parameters.git_sw_catalogs_cred_secret}}"
267           - name: git_volume_name
268             value: '{{steps.generate-sw-catalogs-volume-repo.outputs.parameters.pvc-name}}'
269
270     # === PHASE 4: Debug Information (Optional) ===
271     # Comprehensive debugging information for volume-based workflow troubleshooting
272     # Executed only when debug parameter is set to true
273     - - name: debug-info
274         when: "{{inputs.parameters.debug}} == true"
275         inline:
276           container:
277             image: "{{inputs.parameters.osm_sdk_image_repository}}:{{inputs.parameters.osm_sdk_image_tag}}"
278             args:
279               - "nu"
280               - "-c"
281               - |
282                 print "=== OSM App Modeling Debug Information (Volume-Based) ==="
283                 print $"Command: {{inputs.parameters.command}}"
284                 print $"SDK Image: {{inputs.parameters.osm_sdk_image_repository}}:{{inputs.parameters.osm_sdk_image_tag}}"
285                 print $"Model Volume: {{inputs.parameters.model_volume_name}}"
286                 print $"Secret Name: {{inputs.parameters.secret_name}}"
287                 print $"Timestamp: (date now | format date '%Y-%m-%d %H:%M:%S')"
288                 print ""
289
290                 print "=== Volume Mount Status and Accessibility ==="
291                 print "Checking volume mount accessibility..."
292
293                 # Check model volume mount
294                 print "Model volume mount status:"
295                 if (ls /model | length) > 0 {
296                   print "✓ Model volume mounted successfully at /model"
297                   print $"  Files found: (ls /model | length)"
298                 } else {
299                   print "✗ Model volume mount failed or empty"
300                 }
301
302                 # Check fleet volume mount
303                 print "Fleet volume mount status:"
304                 if (ls /repos/fleet-osm | length) > 0 {
305                   print "✓ Fleet volume mounted successfully at /repos/fleet-osm"
306                   print $"  Files found: (ls /repos/fleet-osm | length)"
307                 } else {
308                   print "✗ Fleet volume mount failed or empty"
309                 }
310
311                 # Check sw-catalogs volume mount
312                 print "SW-Catalogs volume mount status:"
313                 if (ls /repos/sw-catalogs-osm | length) > 0 {
314                   print "✓ SW-Catalogs volume mounted successfully at /repos/sw-catalogs-osm"
315                   print $"  Files found: (ls /repos/sw-catalogs-osm | length)"
316                 } else {
317                   print "✗ SW-Catalogs volume mount failed or empty"
318                 }
319                 print ""
320
321                 print "=== Model Volume Contents and Structure ==="
322                 print "Model volume directory structure:"
323                 try {
324                   ls /model -la | select name type size mode | table
325                 } catch {
326                   print "Error accessing model volume contents"
327                 }
328                 print ""
329
330                 print "=== Required Model Files Validation ==="
331                 # Check app_instance_model.yaml
332                 if ("/model/app_instance_model.yaml" | path exists) {
333                   print "✓ app_instance_model.yaml found"
334                   try {
335                     let file_size = (ls /model/app_instance_model.yaml | get size | first)
336                     print $"  Size: ($file_size) bytes"
337                   } catch {
338                     print "  Warning: Could not read file size"
339                   }
340                 } else {
341                   print "✗ app_instance_model.yaml missing"
342                 }
343
344                 # Check clear parameters
345                 if ("/model/parameters/clear/environment.yaml" | path exists) {
346                   print "✓ Clear parameters file found"
347                   try {
348                     let file_size = (ls /model/parameters/clear/environment.yaml | get size | first)
349                     print $"  Size: ($file_size) bytes"
350                   } catch {
351                     print "  Warning: Could not read file size"
352                   }
353                 } else {
354                   print "ℹ Clear parameters file missing (optional)"
355                 }
356
357                 # Check secret parameters
358                 if ("/model/parameters/secret/environment.yaml" | path exists) {
359                   print "✓ Secret parameters file found"
360                   try {
361                     let file_size = (ls /model/parameters/secret/environment.yaml | get size | first)
362                     print $"  Size: ($file_size) bytes"
363                   } catch {
364                     print "  Warning: Could not read file size"
365                   }
366                 } else {
367                   print "ℹ Secret parameters file missing (optional)"
368                 }
369
370                 print "Clear Parameters Content:"
371                 try {
372                   let clear_params = open /model/parameters/clear/environment.yaml | to yaml
373                   print $clear_params
374                 } catch {
375                   print "Clear parameters not available"
376                 }
377                 print ""
378
379                 print "Secret Parameters Content (masked for security):"
380                 try {
381                   if ("/model/parameters/secret/environment.yaml" | path exists) {
382                     print "Secret file exists - content masked for security"
383                   } else {
384                     print "Secret parameters not available"
385                   }
386                 } catch {
387                   print "Error checking secret parameters"
388                 }
389                 print ""
390
391                 print "=== Loaded Model Instance from Volume ==="
392                 try {
393                   $model_instance | to yaml
394                   print $model_instance
395                 } catch {
396                   print "Error loading model instance"
397                 }
398                 print ""
399
400                 print "=== Environment Parameters from Volume ==="
401                 try {
402                   $environment | to yaml
403                   print $environment
404                 } catch {
405                   print "Error loading environment"
406                 }
407                 print ""
408
409                 print "=== Repository Structure ==="
410                 print "Fleet repo contents:"
411                 try {
412                   ls /repos/fleet-osm | select name type size | first 10 | table
413                 } catch {
414                   print "Error accessing fleet repository"
415                 }
416                 print ""
417                 print "SW-Catalogs repo contents:"
418                 try {
419                   ls /repos/sw-catalogs-osm | select name type size | first 10 | table
420                 } catch {
421                   print "Error accessing sw-catalogs repository"
422                 }
423                 print ""
424
425                 print "=== Embedded Operations Validation ==="
426                 print "App Operations Library Status:"
427                 try {
428                   help app | lines | first 10 | str join "\n"
429                 } catch {
430                   print "Error accessing app operations library"
431                 }
432                 print ""
433
434                 print "=== Dry Run Test ==="
435                 print "Testing command execution with volume-mounted data..."
436                 try {
437                   print $"Command to execute: {{inputs.parameters.command}}"
438                   print "✓ Command validation successful with volume-based data"
439                 } catch {
440                   print $"✗ Command validation failed: ($in)"
441                 }
442                 print ""
443
444                 print "=== Debug Information Complete ==="
445             volumeMounts:
446             - name: model-volume
447               mountPath: /model
448             - name: fleet-volume
449               mountPath: /repos/fleet-osm
450               subPath: fleet-osm
451             - name: sw-catalogs-volume
452               mountPath: /repos/sw-catalogs-osm
453               subPath: sw-catalogs-osm
454             # Mount secret volume when secret_name is provided
455             - name: secret-volume
456               mountPath: /model/parameters/secret
457               readOnly: true
458           volumes:
459           - name: model-volume
460             persistentVolumeClaim:
461               claimName: "{{inputs.parameters.model_volume_name}}"
462           - name: fleet-volume
463             persistentVolumeClaim:
464               claimName: '{{steps.generate-fleet-volume-repo.outputs.parameters.pvc-name}}'
465           - name: sw-catalogs-volume
466             persistentVolumeClaim:
467               claimName: '{{steps.generate-sw-catalogs-volume-repo.outputs.parameters.pvc-name}}'
468           - name: secret-volume
469             secret:
470               secretName: "{{inputs.parameters.secret_name}}"
471               optional: true
472
473     # === PHASE 5: Application Operations ===
474     # This phase performs the actual create/update/delete operations on the application model
475     - - name: run-app-operation
476         when: "{{inputs.parameters.dry_run}} != true"
477         inline:
478           container:
479             image: "{{inputs.parameters.osm_sdk_image_repository}}:{{inputs.parameters.osm_sdk_image_tag}}"
480             args: ["{{inputs.parameters.command}}"]         # Full command string to execute
481             volumeMounts:
482             # Mount model volume containing application data
483             - name: model-volume
484               mountPath: /model
485             # Mount Fleet repository for GitOps target
486             - name: fleet-volume
487               mountPath: /repos/fleet-osm
488               subPath: fleet-osm
489             # Mount SW-Catalogs repository for application templates
490             - name: sw-catalogs-volume
491               mountPath: /repos/sw-catalogs-osm
492               subPath: sw-catalogs-osm
493             # Mount secret volume when secret_name is provided
494             - name: secret-volume
495               mountPath: /model/parameters/secret
496               readOnly: true
497           volumes:
498           - name: model-volume
499             persistentVolumeClaim:
500               claimName: "{{inputs.parameters.model_volume_name}}"
501           - name: fleet-volume
502             persistentVolumeClaim:
503               claimName: '{{steps.generate-fleet-volume-repo.outputs.parameters.pvc-name}}'
504           - name: sw-catalogs-volume
505             persistentVolumeClaim:
506               claimName: '{{steps.generate-sw-catalogs-volume-repo.outputs.parameters.pvc-name}}'
507           - name: secret-volume
508             secret:
509               secretName: "{{inputs.parameters.secret_name}}"
510               optional: true
511       # Dry-run variant of the app operation for validation without changes
512       - name: run-app-operation-dry-run
513         when: "{{inputs.parameters.dry_run}} == true"
514         inline:
515           container:
516             image: "{{inputs.parameters.osm_sdk_image_repository}}:{{inputs.parameters.osm_sdk_image_tag}}"
517             args: ["{{inputs.parameters.command}} --dry-run"] # Full command string with dry-run flag
518             volumeMounts:
519             # Same volume mounts as regular operation for consistency
520             - name: model-volume
521               mountPath: /model
522             - name: fleet-volume
523               mountPath: /repos/fleet-osm
524               subPath: fleet-osm
525             - name: sw-catalogs-volume
526               mountPath: /repos/sw-catalogs-osm
527               subPath: sw-catalogs-osm
528             # Mount secret volume when secret_name is provided
529             - name: secret-volume
530               mountPath: /model/parameters/secret
531               readOnly: true
532           volumes:
533           - name: model-volume
534             persistentVolumeClaim:
535               claimName: "{{inputs.parameters.model_volume_name}}"
536           - name: fleet-volume
537             persistentVolumeClaim:
538               claimName: '{{steps.generate-fleet-volume-repo.outputs.parameters.pvc-name}}'
539           - name: sw-catalogs-volume
540             persistentVolumeClaim:
541               claimName: '{{steps.generate-sw-catalogs-volume-repo.outputs.parameters.pvc-name}}'
542           - name: secret-volume
543             secret:
544               secretName: "{{inputs.parameters.secret_name}}"
545               optional: true
546
547     # === PHASE 7: GitOps Commit and Push ===
548     # Commit and push the generated application manifests to the Fleet repository
549     # This completes the GitOps workflow by updating the target repository with changes
550     - - name: push-to-fleet
551         templateRef:
552           name: git-wft
553           template: git-commit-merge-push
554         arguments:
555           parameters:
556           - name: mount_path
557             value: "/fleet"
558           - name: repo_folder
559             value: "{{inputs.parameters.fleet_destination_folder}}"
560           - name: git_cred_secret
561             value: "{{inputs.parameters.git_fleet_cred_secret}}"
562           - name: git_volume_name
563             value: '{{steps.generate-fleet-volume-repo.outputs.parameters.pvc-name}}'
564           - name: commit_message
565             value: "Execute '{{inputs.parameters.command}}' for App '{{inputs.parameters.app_name}}' in {{inputs.parameters.profile_name}} profile ({{inputs.parameters.profile_type}}) @ {{inputs.parameters.project_name}} project [Volume-Based Workflow]"
566           - name: main_branch
567             value: main
568           - name: contrib_branch
569             value: osm_contrib
570           - name: dry_run
571             value: "{{inputs.parameters.dry_run}}"