Compare commits
	
		
			53 Commits
		
	
	
		
			ade18f1042
			...
			feature/mi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d62a20ab39 | ||
| 
						 | 
					6bacf44f2f | ||
| 
						 | 
					0b652f5dfc | ||
| 
						 | 
					a50ede2688 | ||
| 
						 | 
					722a42e36d | ||
| 
						 | 
					a46708842b | ||
| 
						 | 
					1e4011d5b1 | ||
| 
						 | 
					f5e1707545 | ||
| 
						 | 
					cd6d5a24aa | ||
| 
						 | 
					24bbe81638 | ||
| 
						 | 
					7c913bec0e | ||
| 
						 | 
					bdbbd7697a | ||
| 
						 | 
					6917295fbd | ||
| 
						 | 
					e1b0ad089c | ||
| 
						 | 
					483f747754 | ||
| 
						 | 
					03675d09ae | ||
| 
						 | 
					f3e84a4f43 | ||
| 
						 | 
					eae5474552 | ||
| 
						 | 
					bae9cb2011 | ||
| 
						 | 
					65b8960703 | ||
| 
						 | 
					90aa19caeb | ||
| 
						 | 
					dcb3e2b7cc | ||
| 
						 | 
					c871d68333 | ||
| 
						 | 
					6cf5da787a | ||
| 
						 | 
					fa4db92c92 | ||
| 
						 | 
					ee94c1aa42 | ||
| 
						 | 
					c40b18f1d6 | ||
| 
						 | 
					2932fb2710 | ||
| 
						 | 
					2343a5329e | ||
| 
						 | 
					86fa41a376 | ||
| 
						 | 
					6ec7a670bd | ||
| 6323d4eed4 | |||
| 93f3806b86 | |||
| 
						 | 
					b3524ccfad | ||
| 
						 | 
					e2d1746396 | ||
| 
						 | 
					5f70feab59 | ||
| 
						 | 
					fb8d994be3 | ||
| 
						 | 
					6f7acee2df | ||
| 
						 | 
					31580f1905 | ||
| 
						 | 
					04d6001fec | ||
| 
						 | 
					e2ceb6e58d | ||
| 
						 | 
					cd804fbeb5 | ||
| 
						 | 
					9aefa18ea8 | ||
| 
						 | 
					27fd603e36 | ||
| 
						 | 
					c31184e2ec | ||
| 
						 | 
					5d8143c93e | ||
| 
						 | 
					77a9b0770e | ||
| 
						 | 
					9a17623cab | ||
| 
						 | 
					4963284056 | ||
| 
						 | 
					df09585cc9 | ||
| 
						 | 
					aa20edaf25 | ||
| 42ee6abcb6 | |||
| 08ade1af66 | 
@@ -24,3 +24,5 @@ FROM scratch
 | 
				
			|||||||
WORKDIR /app
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY --from=builder /app/oc-monitord .
 | 
					COPY --from=builder /app/oc-monitord .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENTRYPOINT ["./oc-monitord"]
 | 
				
			||||||
							
								
								
									
										71
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								README.md
									
									
									
									
									
								
							@@ -3,64 +3,43 @@
 | 
				
			|||||||
DO :
 | 
					DO :
 | 
				
			||||||
  make build
 | 
					  make build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Deploy in k8s (dev)
 | 
					## Summary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
While a registry with all of the OC docker images has not been set-up we can export this image to k3s ctr
 | 
					oc-monitord is a daemon which can be run :
 | 
				
			||||||
 | 
					- as a binary
 | 
				
			||||||
 | 
					- as a container
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> docker save oc-monitord:latest | sudo k3s ctr images import -
 | 
					It is used to perform several actions regarding the execution of an Open Cloud workflow :
 | 
				
			||||||
 | 
					- generating a YAML file that can be interpreted by **Argo Workflow** to create and execute pods in a kubernetes environment
 | 
				
			||||||
 | 
					- setting up the different resources needed to execute a workflow over several peers/kubernetes nodes with **Admiralty** : token, secrets, targets and sources
 | 
				
			||||||
 | 
					- creating the workflow and logging the output from 
 | 
				
			||||||
 | 
					  - Argo watch, which gives informations about the workflow in general (phase, number of steps executed, status...)
 | 
				
			||||||
 | 
					  - Pods : which are the logs generated by the pods 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Then in the pod manifest for oc-monitord use : 
 | 
					To execute, the daemon needs several options : 
 | 
				
			||||||
 | 
					- **-u** :  
 | 
				
			||||||
 | 
					- **-m** :
 | 
				
			||||||
 | 
					- **-d** :
 | 
				
			||||||
 | 
					- **-e** :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					# Notes features/admiralty-docker
 | 
				
			||||||
image: docker.io/library/oc-monitord
 | 
					 | 
				
			||||||
imagePullPolicy: Never
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Not doing so will end up in the pod having a `ErrorImagePull`
 | 
					- When executing monitord as a container we need to change any url with "localhost" to the container's host IP. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Allow argo to create services
 | 
					  We can : 
 | 
				
			||||||
 | 
					  - declare a new parameter 'HOST_IP'
 | 
				
			||||||
In order for monitord to expose **open cloud services** on the node, we need to give him permission to create **k8s services**.
 | 
					  - decide that no peer can have "http://localhost" as its url and use an attribute from the peer object or isMyself() from oc-lib if a peer is the current host.
 | 
				
			||||||
 | 
					 | 
				
			||||||
For that we can update the RBAC configuration for a role already created by argo : 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Manually edit the rbac authorization 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> kubectl edit roles.rbac.authorization.k8s.io -n argo argo-role
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In rules add a new entry : 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
- apiGroups:
 | 
					 | 
				
			||||||
  - ""
 | 
					 | 
				
			||||||
  resources:  
 | 
					 | 
				
			||||||
  - services
 | 
					 | 
				
			||||||
    verbs:                                                                                                                                                          
 | 
					 | 
				
			||||||
    - get
 | 
					 | 
				
			||||||
    - create
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Patch the rbac authorization with a one liner 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> kubectl patch role argo-role -n argo --type='json' -p='[{"op": "add", "path": "/rules/-", "value": {"apiGroups": [""], "resources": ["services"], "verbs": ["get","create"]}}]'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Check wether the modification is effective 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> kubectl auth can-i create services --as=system:serviceaccount:argo:argo -n argo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This command **must return "yes"**
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## TODO
 | 
					## TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [ ] Logs the output of each pods : 
 | 
					- [ ] Allow the front to known on which IP the service are reachable
 | 
				
			||||||
  - logsPods() function already exists
 | 
					 | 
				
			||||||
  - need to implement the logic to create each pod's logger and start the monitoring routing
 | 
					 | 
				
			||||||
- [ ] Allow the front to known on which IP the service are reachable
 | 
					 | 
				
			||||||
  - currently doing it by using `kubectl get nodes -o wide`
 | 
					  - currently doing it by using `kubectl get nodes -o wide`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [ ] Implement writing and reading from S3 bucket/MinIO when a data resource is linked to a compute resource.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Adding ingress handling to support reverse proxing
 | 
					
 | 
				
			||||||
 | 
					### Adding ingress handling to support reverse proxing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Test wether ingress-nginx is running or not
 | 
					- Test wether ingress-nginx is running or not
 | 
				
			||||||
  - Do something if not found : stop running and send error log OR start installation
 | 
					  - Do something if not found : stop running and send error log OR start installation
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ type Config struct {
 | 
				
			|||||||
	KubeCA      string
 | 
						KubeCA      string
 | 
				
			||||||
	KubeCert    string
 | 
						KubeCert    string
 | 
				
			||||||
	KubeData    string
 | 
						KubeData    string
 | 
				
			||||||
 | 
						ArgoHost	string 		// when executed in a container will replace addresses with "localhost" in their url
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var instance *Config
 | 
					var instance *Config
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								docs/admiralty_naming_multi_peer.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/admiralty_naming_multi_peer.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 71 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/admiralty_setup_schema.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/admiralty_setup_schema.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 91 KiB  | 
							
								
								
									
										4
									
								
								env.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								env.env
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					KUBERNETES_SERVICE_HOST=192.168.1.169
 | 
				
			||||||
 | 
					KUBE_CA="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTWpNeE1USXdNell3SGhjTk1qUXdPREE0TVRBeE16VTJXaGNOTXpRd09EQTJNVEF4TXpVMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTWpNeE1USXdNell3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTVlk3ZHZhNEdYTVdkMy9jMlhLN3JLYjlnWXgyNSthaEE0NmkyNVBkSFAKRktQL2UxSVMyWVF0dzNYZW1TTUQxaStZdzJSaVppNUQrSVZUamNtNHdhcnFvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWtlUVJpNFJiODduME5yRnZaWjZHClc2SU55NnN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnRXA5ck04WmdNclRZSHYxZjNzOW5DZXZZeWVVa3lZUk4KWjUzazdoaytJS1FDSVFDbk05TnVGKzlTakIzNDFacGZ5ays2NEpWdkpSM3BhcmVaejdMd2lhNm9kdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
 | 
				
			||||||
 | 
					KUBE_CERT="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJWUxWNkFPQkdrU1F3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOekl6TVRFeU1ETTJNQjRYRFRJME1EZ3dPREV3TVRNMU5sb1hEVEkxTURndwpPREV3TVRNMU5sb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJGQ2Q1MFdPeWdlQ2syQzcKV2FrOWY4MVAvSkJieVRIajRWOXBsTEo0ck5HeHFtSjJOb2xROFYxdUx5RjBtOTQ2Nkc0RmRDQ2dqaXFVSk92Swp3NVRPNnd5alNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCVFJkOFI5cXVWK2pjeUVmL0ovT1hQSzMyS09XekFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQTArbThqTDBJVldvUTZ0dnB4cFo4NVlMalF1SmpwdXM0aDdnSXRxS3NmUVVDSUI2M2ZNdzFBMm5OVWU1TgpIUGZOcEQwSEtwcVN0Wnk4djIyVzliYlJUNklZCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTWpNeE1USXdNell3SGhjTk1qUXdPREE0TVRBeE16VTJXaGNOTXpRd09EQTJNVEF4TXpVMgpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTWpNeE1USXdNell3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFRc3hXWk9pbnIrcVp4TmFEQjVGMGsvTDF5cE01VHAxOFRaeU92ektJazQKRTFsZWVqUm9STW0zNmhPeVljbnN3d3JoNnhSUnBpMW5RdGhyMzg0S0Z6MlBvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVTBYZkVmYXJsZm8zTWhIL3lmemx6Cnl0OWlqbHN3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUxJL2dNYnNMT3MvUUpJa3U2WHVpRVMwTEE2cEJHMXgKcnBlTnpGdlZOekZsQWlFQW1wdjBubjZqN3M0MVI0QzFNMEpSL0djNE53MHdldlFmZWdEVGF1R2p3cFk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
 | 
				
			||||||
 | 
					KUBE_DATA="LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5ZS1BFb1dhd1NKUzJlRW5oWmlYMk5VZlY1ZlhKV2krSVNnV09TNFE5VTlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVUozblJZN0tCNEtUWUx0WnFUMS96VS84a0Z2Sk1lUGhYMm1Vc25pczBiR3FZblkyaVZEeApYVzR2SVhTYjNqcm9iZ1YwSUtDT0twUWs2OHJEbE03ckRBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="
 | 
				
			||||||
							
								
								
									
										57
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								go.mod
									
									
									
									
									
								
							@@ -5,21 +5,22 @@ go 1.23.1
 | 
				
			|||||||
toolchain go1.23.3
 | 
					toolchain go1.23.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	cloud.o-forge.io/core/oc-lib v0.0.0-20250313155727-88c88cac5bc9
 | 
						cloud.o-forge.io/core/oc-lib v0.0.0-20250715125819-e735f78e58c6
 | 
				
			||||||
	github.com/akamensky/argparse v1.4.0
 | 
						github.com/akamensky/argparse v1.4.0
 | 
				
			||||||
	github.com/google/uuid v1.6.0
 | 
						github.com/google/uuid v1.6.0
 | 
				
			||||||
	github.com/goraz/onion v0.1.3
 | 
						github.com/goraz/onion v0.1.3
 | 
				
			||||||
	github.com/nwtgck/go-fakelish v0.1.3
 | 
						github.com/nwtgck/go-fakelish v0.1.3
 | 
				
			||||||
	github.com/rs/zerolog v1.33.0
 | 
						github.com/rs/zerolog v1.34.0
 | 
				
			||||||
	gopkg.in/yaml.v3 v3.0.1
 | 
						gopkg.in/yaml.v3 v3.0.1
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/beego/beego/v2 v2.3.1 // indirect
 | 
						github.com/beego/beego/v2 v2.3.8 // indirect
 | 
				
			||||||
	github.com/go-playground/validator/v10 v10.22.0 // indirect
 | 
						github.com/go-playground/validator/v10 v10.27.0 // indirect
 | 
				
			||||||
	github.com/golang/protobuf v1.5.4 // indirect
 | 
						github.com/golang/protobuf v1.5.4 // indirect
 | 
				
			||||||
	github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
 | 
						github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
 | 
				
			||||||
	github.com/sirupsen/logrus v1.9.3 // indirect
 | 
						github.com/sirupsen/logrus v1.9.3 // indirect
 | 
				
			||||||
 | 
						github.com/ugorji/go/codec v1.1.7 // indirect
 | 
				
			||||||
	google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
 | 
						google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
 | 
				
			||||||
	google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
 | 
						google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
 | 
				
			||||||
	google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
 | 
						google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
 | 
				
			||||||
@@ -27,15 +28,14 @@ require (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
 | 
					 | 
				
			||||||
	github.com/argoproj/argo-workflows/v3 v3.6.4
 | 
						github.com/argoproj/argo-workflows/v3 v3.6.4
 | 
				
			||||||
	github.com/beorn7/perks v1.0.1 // indirect
 | 
						github.com/beorn7/perks v1.0.1 // indirect
 | 
				
			||||||
	github.com/biter777/countries v1.7.5 // indirect
 | 
						github.com/biter777/countries v1.7.5 // indirect
 | 
				
			||||||
	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 | 
						github.com/cespare/xxhash/v2 v2.3.0 // indirect
 | 
				
			||||||
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 | 
						github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 | 
				
			||||||
	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
 | 
						github.com/emicklei/go-restful/v3 v3.11.0 // indirect
 | 
				
			||||||
	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
 | 
						github.com/fxamacker/cbor/v2 v2.7.0 // indirect
 | 
				
			||||||
	github.com/gabriel-vasile/mimetype v1.4.5 // indirect
 | 
						github.com/gabriel-vasile/mimetype v1.4.9 // indirect
 | 
				
			||||||
	github.com/go-logr/logr v1.4.2 // indirect
 | 
						github.com/go-logr/logr v1.4.2 // indirect
 | 
				
			||||||
	github.com/go-openapi/jsonpointer v0.21.0 // indirect
 | 
						github.com/go-openapi/jsonpointer v0.21.0 // indirect
 | 
				
			||||||
	github.com/go-openapi/jsonreference v0.20.4 // indirect
 | 
						github.com/go-openapi/jsonreference v0.20.4 // indirect
 | 
				
			||||||
@@ -43,50 +43,49 @@ require (
 | 
				
			|||||||
	github.com/go-playground/locales v0.14.1 // indirect
 | 
						github.com/go-playground/locales v0.14.1 // indirect
 | 
				
			||||||
	github.com/go-playground/universal-translator v0.18.1 // indirect
 | 
						github.com/go-playground/universal-translator v0.18.1 // indirect
 | 
				
			||||||
	github.com/gogo/protobuf v1.3.2 // indirect
 | 
						github.com/gogo/protobuf v1.3.2 // indirect
 | 
				
			||||||
	github.com/golang/snappy v0.0.4 // indirect
 | 
						github.com/golang/snappy v1.0.0 // indirect
 | 
				
			||||||
	github.com/google/gnostic-models v0.6.8 // indirect
 | 
						github.com/google/gnostic-models v0.6.8 // indirect
 | 
				
			||||||
	github.com/google/go-cmp v0.6.0 // indirect
 | 
						github.com/google/go-cmp v0.7.0 // indirect
 | 
				
			||||||
	github.com/google/gofuzz v1.2.0 // indirect
 | 
						github.com/google/gofuzz v1.2.0 // indirect
 | 
				
			||||||
	github.com/hashicorp/golang-lru v0.5.4 // indirect
 | 
						github.com/hashicorp/golang-lru v1.0.2 // indirect
 | 
				
			||||||
	github.com/josharian/intern v1.0.0 // indirect
 | 
						github.com/josharian/intern v1.0.0 // indirect
 | 
				
			||||||
	github.com/json-iterator/go v1.1.12 // indirect
 | 
						github.com/json-iterator/go v1.1.12 // indirect
 | 
				
			||||||
	github.com/klauspost/compress v1.17.10 // indirect
 | 
						github.com/klauspost/compress v1.18.0 // indirect
 | 
				
			||||||
	github.com/leodido/go-urn v1.4.0 // indirect
 | 
						github.com/leodido/go-urn v1.4.0 // indirect
 | 
				
			||||||
	github.com/mailru/easyjson v0.7.7 // indirect
 | 
						github.com/mailru/easyjson v0.7.7 // indirect
 | 
				
			||||||
	github.com/mattn/go-colorable v0.1.13 // indirect
 | 
						github.com/mattn/go-colorable v0.1.14 // indirect
 | 
				
			||||||
	github.com/mattn/go-isatty v0.0.20 // indirect
 | 
						github.com/mattn/go-isatty v0.0.20 // indirect
 | 
				
			||||||
	github.com/mitchellh/mapstructure v1.5.0 // indirect
 | 
						github.com/mitchellh/mapstructure v1.5.0 // indirect
 | 
				
			||||||
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
						github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
				
			||||||
	github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
						github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
				
			||||||
	github.com/montanaflynn/stats v0.7.1 // indirect
 | 
						github.com/montanaflynn/stats v0.7.1 // indirect
 | 
				
			||||||
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 | 
						github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 | 
				
			||||||
	github.com/nats-io/nats.go v1.37.0 // indirect
 | 
						github.com/nats-io/nats.go v1.43.0 // indirect
 | 
				
			||||||
	github.com/nats-io/nkeys v0.4.7 // indirect
 | 
						github.com/nats-io/nkeys v0.4.11 // indirect
 | 
				
			||||||
	github.com/nats-io/nuid v1.0.1 // indirect
 | 
						github.com/nats-io/nuid v1.0.1 // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
						github.com/pkg/errors v0.9.1 // indirect
 | 
				
			||||||
	github.com/prometheus/client_golang v1.19.0 // indirect
 | 
						github.com/prometheus/client_golang v1.22.0 // indirect
 | 
				
			||||||
	github.com/prometheus/client_model v0.6.0 // indirect
 | 
						github.com/prometheus/client_model v0.6.2 // indirect
 | 
				
			||||||
	github.com/prometheus/common v0.48.0 // indirect
 | 
						github.com/prometheus/common v0.65.0 // indirect
 | 
				
			||||||
	github.com/prometheus/procfs v0.12.0 // indirect
 | 
						github.com/prometheus/procfs v0.17.0 // indirect
 | 
				
			||||||
	github.com/robfig/cron v1.2.0 // indirect
 | 
						github.com/robfig/cron v1.2.0 // indirect
 | 
				
			||||||
	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
 | 
						github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect
 | 
				
			||||||
	github.com/smartystreets/goconvey v1.6.4 // indirect
 | 
						github.com/smartystreets/goconvey v1.6.4 // indirect
 | 
				
			||||||
	github.com/ugorji/go/codec v1.1.7 // indirect
 | 
					 | 
				
			||||||
	github.com/x448/float16 v0.8.4 // indirect
 | 
						github.com/x448/float16 v0.8.4 // indirect
 | 
				
			||||||
	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 | 
						github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 | 
				
			||||||
	github.com/xdg-go/scram v1.1.2 // indirect
 | 
						github.com/xdg-go/scram v1.1.2 // indirect
 | 
				
			||||||
	github.com/xdg-go/stringprep v1.0.4 // indirect
 | 
						github.com/xdg-go/stringprep v1.0.4 // indirect
 | 
				
			||||||
	github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
 | 
						github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
 | 
				
			||||||
	go.mongodb.org/mongo-driver v1.17.1 // indirect
 | 
						go.mongodb.org/mongo-driver v1.17.4 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.31.0 // indirect
 | 
						golang.org/x/crypto v0.40.0 // indirect
 | 
				
			||||||
	golang.org/x/net v0.33.0 // indirect
 | 
						golang.org/x/net v0.42.0 // indirect
 | 
				
			||||||
	golang.org/x/oauth2 v0.23.0 // indirect
 | 
						golang.org/x/oauth2 v0.30.0 // indirect
 | 
				
			||||||
	golang.org/x/sync v0.10.0 // indirect
 | 
						golang.org/x/sync v0.16.0 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.28.0 // indirect
 | 
						golang.org/x/sys v0.34.0 // indirect
 | 
				
			||||||
	golang.org/x/term v0.27.0 // indirect
 | 
						golang.org/x/term v0.33.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.21.0 // indirect
 | 
						golang.org/x/text v0.27.0 // indirect
 | 
				
			||||||
	golang.org/x/time v0.7.0 // indirect
 | 
						golang.org/x/time v0.7.0 // indirect
 | 
				
			||||||
	google.golang.org/protobuf v1.35.1 // indirect
 | 
						google.golang.org/protobuf v1.36.6 // indirect
 | 
				
			||||||
	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 | 
						gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 | 
				
			||||||
	gopkg.in/inf.v0 v0.9.1 // indirect
 | 
						gopkg.in/inf.v0 v0.9.1 // indirect
 | 
				
			||||||
	k8s.io/api v0.32.1
 | 
						k8s.io/api v0.32.1
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										175
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										175
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,53 +1,29 @@
 | 
				
			|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
					cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
				
			||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
					cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250213085018-271cc2caa026 h1:CYwpofGfpAhMDrT6jqvu9NI/tcgxCD8PKJZDKEfTvVI=
 | 
					cloud.o-forge.io/core/oc-lib v0.0.0-20250604083300-387785b40cb0 h1:iEm/Rf9I0OSCcncuFy61YOSZ3jdRlhJ/oLD97Pc2pCQ=
 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250213085018-271cc2caa026/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					cloud.o-forge.io/core/oc-lib v0.0.0-20250604083300-387785b40cb0/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250213093249-c53e25e69a7b h1:HAb2h0011mE3QrHdOwJCua5w0r/BDOFLNb/557ZAzL0=
 | 
					cloud.o-forge.io/core/oc-lib v0.0.0-20250620085001-583ca2fbacd5 h1:FEBwueVOOWKYf0tJuE0EKNIbjxmTyCMgkT4qATYsfbo=
 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250213093249-c53e25e69a7b/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					cloud.o-forge.io/core/oc-lib v0.0.0-20250620085001-583ca2fbacd5/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250217072519-cafadec1469f h1:esLB0EAn8IuOChW35kcBrPaN80z4A4yYyz1mXT45GQo=
 | 
					cloud.o-forge.io/core/oc-lib v0.0.0-20250704084459-443546027b27 h1:iogk6pV3gybzQDBXMI6Qd/jvSA1h+3oRE+vLl1MRjew=
 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250217072519-cafadec1469f/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					cloud.o-forge.io/core/oc-lib v0.0.0-20250704084459-443546027b27/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218080121-a098f0a672ee h1:UIGIiE+O5LUrP18C8nrZxN1v6Lmzfdlv8pvHnSLKJz8=
 | 
					cloud.o-forge.io/core/oc-lib v0.0.0-20250715125819-e735f78e58c6 h1:Gnkv59Ntl2RebC5tNauXuxyRXLfZ2XAJ0+ujMyFte5U=
 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218080121-a098f0a672ee/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					cloud.o-forge.io/core/oc-lib v0.0.0-20250715125819-e735f78e58c6/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI=
 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218085355-6e6ed4ea2c64 h1:dANQHoMCyp3uioCHnUOpLFiG/UO+biyPUoSelDNJ814=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218085355-6e6ed4ea2c64/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218092508-b771b5d25ee5 h1:EwoctMKdVG1PJHRcBcRKCxgdAxy+TV1T617vxIZwkio=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218092508-b771b5d25ee5/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218101140-6bf058ab5ca4 h1:7om8VD4ZivHA2BKBwvqM98/a7D+MTwppd2FloNBg1Y4=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218101140-6bf058ab5ca4/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218113916-04f7537066c1 h1:on0zLtHo1Jj6FvQ/wuJCc/sxfBfgrd2qTFknpDh3wQM=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218113916-04f7537066c1/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218115549-81d3406305c5 h1:DP/XYrxSOc5ORMGvVNqTvFjxLF4cymUW/d3HIZXKDEk=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218115549-81d3406305c5/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218130229-7c30633bded0 h1:3EsRmeTz6OWHJETrPObctnGF8WgZtXHfwL2cjyHcfOk=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250218130229-7c30633bded0/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250219075511-241c6a5a0861 h1:XqTFKSZ8hXGCJbuu/SBwakpftevg1AKV7hDI50cXNUg=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250219075511-241c6a5a0861/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250219100312-b4a176667754 h1:7J5EUe/iNS6cT6KVDklpgGH7ak30iEFgWJDEPF6wik4=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250219100312-b4a176667754/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250219104152-3ecb0e9d960b h1:DhRqJdw2VePaYVlsh8OUA3zl+76Q0FWwGu+a+3aOf6s=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250219104152-3ecb0e9d960b/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250219142942-5111c9c8bec7 h1:fh6SzBPenzIxufIIzExtx4jEE4OhFposqn3EbHFr92Q=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250219142942-5111c9c8bec7/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250313155727-88c88cac5bc9 h1:mSFFPwil5Ih+RPBvn88MBerQMtsoHnOuyCZQaf91a34=
 | 
					 | 
				
			||||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250313155727-88c88cac5bc9/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE=
 | 
					 | 
				
			||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
					github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
				
			||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
 | 
					 | 
				
			||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
 | 
					 | 
				
			||||||
github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc=
 | 
					github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc=
 | 
				
			||||||
github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
 | 
					github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
 | 
				
			||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 | 
					github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 | 
				
			||||||
github.com/argoproj/argo-workflows/v3 v3.6.4 h1:5+Cc1UwaQE5ka3w7R3hxZ1TK3M6VjDEXA5WSQ/IXrxY=
 | 
					github.com/argoproj/argo-workflows/v3 v3.6.4 h1:5+Cc1UwaQE5ka3w7R3hxZ1TK3M6VjDEXA5WSQ/IXrxY=
 | 
				
			||||||
github.com/argoproj/argo-workflows/v3 v3.6.4/go.mod h1:2f5zB8CkbNCCO1od+kd1dWkVokqcuyvu+tc+Jwx1MZg=
 | 
					github.com/argoproj/argo-workflows/v3 v3.6.4/go.mod h1:2f5zB8CkbNCCO1od+kd1dWkVokqcuyvu+tc+Jwx1MZg=
 | 
				
			||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
					github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
				
			||||||
github.com/beego/beego/v2 v2.3.1 h1:7MUKMpJYzOXtCUsTEoXOxsDV/UcHw6CPbaWMlthVNsc=
 | 
					github.com/beego/beego/v2 v2.3.8 h1:wplhB1pF4TxR+2SS4PUej8eDoH4xGfxuHfS7wAk9VBc=
 | 
				
			||||||
github.com/beego/beego/v2 v2.3.1/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4=
 | 
					github.com/beego/beego/v2 v2.3.8/go.mod h1:8vl9+RrXqvodrl9C8yivX1e6le6deCK6RWeq8R7gTTg=
 | 
				
			||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 | 
					github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 | 
				
			||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 | 
					github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 | 
				
			||||||
github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q=
 | 
					github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q=
 | 
				
			||||||
github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E=
 | 
					github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E=
 | 
				
			||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
					github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
				
			||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
 | 
					github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 | 
				
			||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
					github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
				
			||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
					github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
				
			||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 | 
					github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 | 
				
			||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
					github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
				
			||||||
@@ -73,8 +49,8 @@ github.com/etcd-io/etcd v3.3.17+incompatible/go.mod h1:cdZ77EstHBwVtD6iTgzgvogwc
 | 
				
			|||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
					github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
				
			||||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
 | 
					github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
 | 
				
			||||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
 | 
					github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
 | 
				
			||||||
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
 | 
					github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
 | 
				
			||||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
 | 
					github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
 | 
				
			||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
					github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
				
			||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 | 
					github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 | 
				
			||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 | 
					github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 | 
				
			||||||
@@ -90,8 +66,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 | 
				
			|||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 | 
					github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 | 
				
			||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 | 
					github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 | 
				
			||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 | 
					github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 | 
				
			||||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
 | 
					github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
 | 
				
			||||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
 | 
					github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
 | 
				
			||||||
 | 
					github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
 | 
				
			||||||
 | 
					github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
 | 
				
			||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 | 
					github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 | 
				
			||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 | 
					github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 | 
				
			||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
					github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
				
			||||||
@@ -104,15 +82,15 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 | 
				
			|||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 | 
					github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 | 
				
			||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 | 
					github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 | 
				
			||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 | 
					github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 | 
				
			||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 | 
					github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
 | 
				
			||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
					github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
				
			||||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
 | 
					github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
 | 
				
			||||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
 | 
					github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
 | 
				
			||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
					github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
				
			||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
					github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
				
			||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
					github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
				
			||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
					github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 | 
				
			||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
					github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 | 
				
			||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
					github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
				
			||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 | 
					github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 | 
				
			||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
					github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
				
			||||||
@@ -127,8 +105,8 @@ github.com/goraz/onion v0.1.3 h1:KhyvbDA2b70gcz/d5izfwTiOH8SmrvV43AsVzpng3n0=
 | 
				
			|||||||
github.com/goraz/onion v0.1.3/go.mod h1:XEmz1XoBz+wxTgWB8NwuvRm4RAu3vKxvrmYtzK+XCuQ=
 | 
					github.com/goraz/onion v0.1.3/go.mod h1:XEmz1XoBz+wxTgWB8NwuvRm4RAu3vKxvrmYtzK+XCuQ=
 | 
				
			||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
 | 
					github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
 | 
				
			||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 | 
					github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 | 
					github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 | 
					github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 | 
				
			||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
					github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
				
			||||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
					github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
				
			||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
					github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
				
			||||||
@@ -141,20 +119,23 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
 | 
				
			|||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
					github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
				
			||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 | 
					github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 | 
				
			||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
					github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
				
			||||||
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
 | 
					github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
 | 
				
			||||||
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
 | 
					github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
 | 
				
			||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 | 
					github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 | 
				
			||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 | 
					github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 | 
				
			||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
					github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
				
			||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
					github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
				
			||||||
 | 
					github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 | 
				
			||||||
 | 
					github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 | 
				
			||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
 | 
					github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
 | 
				
			||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
 | 
					github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
 | 
				
			||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
					github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
				
			||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
					github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
				
			||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 | 
					github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 | 
				
			||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 | 
					github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 | 
					 | 
				
			||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 | 
					github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 | 
				
			||||||
 | 
					github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
 | 
				
			||||||
 | 
					github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
					github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
					github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 | 
					github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 | 
				
			||||||
@@ -174,10 +155,10 @@ github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8
 | 
				
			|||||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
 | 
					github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
 | 
				
			||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 | 
					github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 | 
				
			||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 | 
					github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 | 
				
			||||||
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
 | 
					github.com/nats-io/nats.go v1.43.0 h1:uRFZ2FEoRvP64+UUhaTokyS18XBCR/xM2vQZKO4i8ug=
 | 
				
			||||||
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
 | 
					github.com/nats-io/nats.go v1.43.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
 | 
				
			||||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
 | 
					github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
 | 
				
			||||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
 | 
					github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
 | 
				
			||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
 | 
					github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
 | 
				
			||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 | 
					github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 | 
				
			||||||
github.com/nwtgck/go-fakelish v0.1.3 h1:bA8/xa9hQmzppexIhBvdmztcd/PJ4SPuAUTBdMKZ8G4=
 | 
					github.com/nwtgck/go-fakelish v0.1.3 h1:bA8/xa9hQmzppexIhBvdmztcd/PJ4SPuAUTBdMKZ8G4=
 | 
				
			||||||
@@ -195,26 +176,30 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 | 
				
			|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 | 
					github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
 | 
					github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
 | 
				
			||||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
 | 
					github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
 | 
				
			||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
					github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
				
			||||||
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
 | 
					github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
 | 
				
			||||||
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
 | 
					github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
 | 
				
			||||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
 | 
					github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4=
 | 
				
			||||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
 | 
					github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
 | 
				
			||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
 | 
					github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
 | 
				
			||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
 | 
					github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
 | 
				
			||||||
 | 
					github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
 | 
				
			||||||
 | 
					github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
 | 
				
			||||||
 | 
					github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
 | 
				
			||||||
 | 
					github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
 | 
				
			||||||
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
 | 
					github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
 | 
				
			||||||
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
 | 
					github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
 | 
				
			||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 | 
					github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
 | 
					github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
 | 
					github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
 | 
				
			||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 | 
					github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
 | 
				
			||||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
 | 
					github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
 | 
				
			||||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
 | 
					github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
 | 
				
			||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
					github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
				
			||||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik=
 | 
					github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 h1:v9ezJDHA1XGxViAUSIoO/Id7Fl63u6d0YmsAm+/p2hs=
 | 
				
			||||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
 | 
					github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02/go.mod h1:RF16/A3L0xSa0oSERcnhd8Pu3IXSDZSK2gmGIMsttFE=
 | 
				
			||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 | 
					github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 | 
				
			||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 | 
					github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 | 
				
			||||||
github.com/skarademir/naturalsort v0.0.0-20150715044055-69a5d87bef62/go.mod h1:oIdVclZaltY1Nf7OQUkg1/2jImBJ+ZfKZuDIRSwk3p0=
 | 
					github.com/skarademir/naturalsort v0.0.0-20150715044055-69a5d87bef62/go.mod h1:oIdVclZaltY1Nf7OQUkg1/2jImBJ+ZfKZuDIRSwk3p0=
 | 
				
			||||||
@@ -255,16 +240,22 @@ github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfS
 | 
				
			|||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 | 
					github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 | 
				
			||||||
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
 | 
					go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
 | 
				
			||||||
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
 | 
					go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
 | 
				
			||||||
 | 
					go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
 | 
				
			||||||
 | 
					go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
					golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
					golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
					golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
				
			||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
 | 
					golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
 | 
				
			||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
 | 
					golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
 | 
				
			||||||
 | 
					golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
 | 
				
			||||||
 | 
					golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
 | 
				
			||||||
 | 
					golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
 | 
				
			||||||
 | 
					golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
					golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
				
			||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
					golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
				
			||||||
@@ -284,12 +275,16 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
 | 
				
			|||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
					golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
					golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
					golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
				
			||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
 | 
					golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
 | 
				
			||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
 | 
					golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
 | 
				
			||||||
 | 
					golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
 | 
				
			||||||
 | 
					golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
 | 
				
			||||||
 | 
					golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
 | 
				
			||||||
 | 
					golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
 | 
					golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
 | 
				
			||||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 | 
					golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
@@ -297,8 +292,12 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
 | 
				
			|||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
 | 
					golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
 | 
				
			||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 | 
					golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 | 
				
			||||||
 | 
					golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
 | 
				
			||||||
 | 
					golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 | 
				
			||||||
 | 
					golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
 | 
				
			||||||
 | 
					golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
@@ -314,18 +313,26 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
 | 
				
			|||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
 | 
					golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
 | 
				
			||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
					golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
				
			||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
					golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
				
			||||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
 | 
					golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
 | 
				
			||||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
 | 
					golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
 | 
				
			||||||
 | 
					golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
 | 
				
			||||||
 | 
					golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
 | 
				
			||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
					golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
				
			||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 | 
					golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 | 
				
			||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
 | 
					golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
 | 
				
			||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
 | 
					golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
 | 
				
			||||||
 | 
					golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
 | 
				
			||||||
 | 
					golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
 | 
				
			||||||
 | 
					golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
 | 
				
			||||||
 | 
					golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
 | 
				
			||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
 | 
					golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
 | 
				
			||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 | 
					golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
@@ -362,8 +369,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
 | 
				
			|||||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 | 
					google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 | 
				
			||||||
google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
 | 
					google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
 | 
				
			||||||
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
 | 
					google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
 | 
				
			||||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
 | 
					google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
 | 
				
			||||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 | 
					google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
					gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
					gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										235
									
								
								logger/argo_logs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								logger/argo_logs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,235 @@
 | 
				
			|||||||
 | 
					package logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"oc-monitord/tools"
 | 
				
			||||||
 | 
						"oc-monitord/utils"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/rs/zerolog"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/watch"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// An object to monitor the logs generated by a specific pod from a workflow execution
 | 
				
			||||||
 | 
					type ArgoWatch struct {
 | 
				
			||||||
 | 
						Name      string
 | 
				
			||||||
 | 
						Namespace string
 | 
				
			||||||
 | 
						Status    string
 | 
				
			||||||
 | 
						Conditions
 | 
				
			||||||
 | 
						Created  string
 | 
				
			||||||
 | 
						Started  string
 | 
				
			||||||
 | 
						Duration string
 | 
				
			||||||
 | 
						Progress string
 | 
				
			||||||
 | 
						Logs     []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Conditions struct {
 | 
				
			||||||
 | 
						PodRunning bool
 | 
				
			||||||
 | 
						Completed  bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *ArgoWatch) Equals(arg *ArgoWatch) bool {
 | 
				
			||||||
 | 
						if arg == nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return a.Status == arg.Status && a.Progress == arg.Progress && a.Conditions.PodRunning == arg.Conditions.PodRunning && a.Conditions.Completed == arg.Conditions.Completed
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewArgoLogs(name string, namespace string, stepMax int) *ArgoLogs {
 | 
				
			||||||
 | 
						return &ArgoLogs{
 | 
				
			||||||
 | 
							Name:        "oc-monitor-" + name,
 | 
				
			||||||
 | 
							Namespace:   namespace,
 | 
				
			||||||
 | 
							CreatedDate: time.Now().Format("2006-01-02 15:04:05"),
 | 
				
			||||||
 | 
							StepCount:   0,
 | 
				
			||||||
 | 
							StepMax:     stepMax,
 | 
				
			||||||
 | 
							stop:        false,
 | 
				
			||||||
 | 
							Seen:        []string{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// An object to monitor and log the output of an argo submit 
 | 
				
			||||||
 | 
					type ArgoLogs struct {
 | 
				
			||||||
 | 
						Name        string
 | 
				
			||||||
 | 
						Namespace   string
 | 
				
			||||||
 | 
						CreatedDate string
 | 
				
			||||||
 | 
						StepCount   int
 | 
				
			||||||
 | 
						StepMax     int
 | 
				
			||||||
 | 
						stop        bool
 | 
				
			||||||
 | 
						Started     time.Time
 | 
				
			||||||
 | 
						Seen        []string
 | 
				
			||||||
 | 
						Logs        []string
 | 
				
			||||||
 | 
						IsStreaming bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *ArgoLogs) NewWatch() *ArgoWatch {
 | 
				
			||||||
 | 
						return &ArgoWatch{
 | 
				
			||||||
 | 
							Name:      a.Name,
 | 
				
			||||||
 | 
							Namespace: a.Namespace,
 | 
				
			||||||
 | 
							Status:    "Pending",
 | 
				
			||||||
 | 
							Created:   a.CreatedDate,
 | 
				
			||||||
 | 
							Started:   a.Started.Format("2006-01-02 15:04:05"),
 | 
				
			||||||
 | 
							Conditions: Conditions{
 | 
				
			||||||
 | 
								PodRunning: a.StepCount > 0 && a.StepCount < a.StepMax,
 | 
				
			||||||
 | 
								Completed:  a.StepCount == a.StepMax,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Progress: fmt.Sprintf("%v/%v", a.StepCount, a.StepMax),
 | 
				
			||||||
 | 
							Duration: "0s",
 | 
				
			||||||
 | 
							Logs:     []string{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *ArgoLogs) StartStepRecording(current_watch *ArgoWatch, logger zerolog.Logger) {
 | 
				
			||||||
 | 
						jsonified, _ := json.Marshal(current_watch)
 | 
				
			||||||
 | 
						logger.Info().Msg(string(jsonified))
 | 
				
			||||||
 | 
						a.StepCount += 1
 | 
				
			||||||
 | 
						a.Started = time.Now()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ArgoPodLog struct {
 | 
				
			||||||
 | 
						PodName		string
 | 
				
			||||||
 | 
						Step		string
 | 
				
			||||||
 | 
						Message		string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewArgoPodLog(name string, step string, msg string) ArgoPodLog {
 | 
				
			||||||
 | 
						return ArgoPodLog{
 | 
				
			||||||
 | 
							PodName: name,
 | 
				
			||||||
 | 
							Step: step,
 | 
				
			||||||
 | 
							Message: msg,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func LogKubernetesArgo(wfName string, namespace string, watcher watch.Interface) {
 | 
				
			||||||
 | 
						var argoWatcher *ArgoWatch
 | 
				
			||||||
 | 
						var pods []string
 | 
				
			||||||
 | 
						var node wfv1.NodeStatus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wfl := utils.GetWFLogger("")
 | 
				
			||||||
 | 
						wfl.Debug().Msg("Starting to log " + wfName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var wg sync.WaitGroup
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for event := range (watcher.ResultChan()) {
 | 
				
			||||||
 | 
							wf, ok := event.Object.(*wfv1.Workflow)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								wfl.Error().Msg("unexpected type")
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(wf.Status.Nodes) == 0 {
 | 
				
			||||||
 | 
								wfl.Info().Msg("No node status yet")	// The first output of the channel doesn't contain Nodes so we skip it
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							conditions := retrieveCondition(wf)	
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Retrieving the Status for the main node, which is named after the workflow
 | 
				
			||||||
 | 
							if node, ok = wf.Status.Nodes[wfName]; !ok {
 | 
				
			||||||
 | 
								bytified, _ := json.MarshalIndent(wf.Status.Nodes,"","\t") 
 | 
				
			||||||
 | 
								wfl.Fatal().Msg("Could not find the " + wfName + " node in \n" + string(bytified))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							now := time.Now()
 | 
				
			||||||
 | 
							start, _ := time.Parse(time.RFC3339, node.StartedAt.String() )
 | 
				
			||||||
 | 
							duration := now.Sub(start)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newWatcher := ArgoWatch{
 | 
				
			||||||
 | 
								Name: node.Name,
 | 
				
			||||||
 | 
								Namespace: namespace,
 | 
				
			||||||
 | 
								Status: string(node.Phase),
 | 
				
			||||||
 | 
								Created: node.StartedAt.String(),
 | 
				
			||||||
 | 
								Started: node.StartedAt.String(),
 | 
				
			||||||
 | 
								Progress: string(node.Progress),
 | 
				
			||||||
 | 
								Duration: duration.String(),
 | 
				
			||||||
 | 
								Conditions: conditions,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if argoWatcher == nil {
 | 
				
			||||||
 | 
								argoWatcher = &newWatcher
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !newWatcher.Equals(argoWatcher){
 | 
				
			||||||
 | 
								jsonified, _ := json.Marshal(newWatcher)
 | 
				
			||||||
 | 
								wfl.Info().Msg(string(jsonified))
 | 
				
			||||||
 | 
								argoWatcher = &newWatcher
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							// I don't think we need to use WaitGroup here, because the loop itself 
 | 
				
			||||||
 | 
							// acts as blocking process for the main thread, because Argo watch never closes the channel
 | 
				
			||||||
 | 
							for _, pod := range wf.Status.Nodes{
 | 
				
			||||||
 | 
								if !slices.Contains(pods,pod.Name){
 | 
				
			||||||
 | 
									pl := wfl.With().Str("pod",  pod.Name).Logger()
 | 
				
			||||||
 | 
									if wfName == pod.Name { pods = append(pods, pod.Name); continue }	// One of the node is the Workflow, the others are the pods so don't try to log on the wf name
 | 
				
			||||||
 | 
									pl.Info().Msg("Found a new pod to log : "  + pod.Name)
 | 
				
			||||||
 | 
									wg.Add(1)
 | 
				
			||||||
 | 
									go logKubernetesPods(namespace, wfName, pod.Name, pl, &wg)
 | 
				
			||||||
 | 
									pods = append(pods, pod.Name)
 | 
				
			||||||
 | 
								} 
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Stop listening to the chan when the Workflow is completed or something bad happened
 | 
				
			||||||
 | 
							if node.Phase.Completed() {
 | 
				
			||||||
 | 
								wfl.Info().Msg(wfName + " worflow completed")
 | 
				
			||||||
 | 
								wg.Wait()
 | 
				
			||||||
 | 
								wfl.Info().Msg(wfName + " exiting")
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if node.Phase.FailedOrError() {
 | 
				
			||||||
 | 
								wfl.Error().Msg(wfName + "has failed, please refer to the logs")
 | 
				
			||||||
 | 
								wfl.Error().Msg(node.Message)	
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func retrieveCondition(wf *wfv1.Workflow) (c Conditions) {
 | 
				
			||||||
 | 
						for _, cond := range wf.Status.Conditions {
 | 
				
			||||||
 | 
							if cond.Type == "PodRunning" {
 | 
				
			||||||
 | 
								c.PodRunning = cond.Status == "True"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if cond.Type == "Completed" {
 | 
				
			||||||
 | 
								c.Completed = cond.Status == "True"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Function needed to be executed as a go thread 
 | 
				
			||||||
 | 
					func logKubernetesPods(executionId string, wfName string,podName string, logger zerolog.Logger, wg *sync.WaitGroup){
 | 
				
			||||||
 | 
						defer wg.Done()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						s := strings.Split(podName, ".")
 | 
				
			||||||
 | 
						name := s[0] + "-" + s[1]
 | 
				
			||||||
 | 
						step := s[1]
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						k, err := tools.NewKubernetesTool()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Error().Msg("Could not get Kubernetes tools")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						reader, err := k.GetPodLogger(executionId, wfName, podName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Error().Msg(err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(reader)
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							log := scanner.Text()
 | 
				
			||||||
 | 
							podLog := NewArgoPodLog(name,step,log)
 | 
				
			||||||
 | 
							jsonified, _ := json.Marshal(podLog)
 | 
				
			||||||
 | 
							logger.Info().Msg(string(jsonified))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										141
									
								
								logger/local_argo_logs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								logger/local_argo_logs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
				
			|||||||
 | 
					package logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"oc-monitord/conf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"cloud.o-forge.io/core/oc-lib/logs"
 | 
				
			||||||
 | 
						"github.com/rs/zerolog"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var logger zerolog.Logger
 | 
				
			||||||
 | 
					var wfLogger zerolog.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Take the slice of string that make up one round of stderr outputs from the --watch option in argo submit
 | 
				
			||||||
 | 
					func NewLocalArgoWatch(inputs []string) *ArgoWatch {
 | 
				
			||||||
 | 
						var workflow ArgoWatch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, input := range inputs {
 | 
				
			||||||
 | 
							line := strings.TrimSpace(input)
 | 
				
			||||||
 | 
							if line == "" {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case strings.HasPrefix(line, "Name:"):
 | 
				
			||||||
 | 
								workflow.Name = parseValue(line)
 | 
				
			||||||
 | 
							case strings.HasPrefix(line, "Namespace:"):
 | 
				
			||||||
 | 
								workflow.Namespace = parseValue(line)
 | 
				
			||||||
 | 
							case strings.HasPrefix(line, "Status:"):
 | 
				
			||||||
 | 
								workflow.Status = parseValue(line)
 | 
				
			||||||
 | 
							case strings.HasPrefix(line, "PodRunning"):
 | 
				
			||||||
 | 
								workflow.PodRunning = parseBoolValue(line)
 | 
				
			||||||
 | 
							case strings.HasPrefix(line, "Completed"):
 | 
				
			||||||
 | 
								workflow.Completed = parseBoolValue(line)
 | 
				
			||||||
 | 
							case strings.HasPrefix(line, "Created:"):
 | 
				
			||||||
 | 
								workflow.Created = parseValue(line)
 | 
				
			||||||
 | 
							case strings.HasPrefix(line, "Started:"):
 | 
				
			||||||
 | 
								workflow.Started = parseValue(line)
 | 
				
			||||||
 | 
							case strings.HasPrefix(line, "Duration:"):
 | 
				
			||||||
 | 
								workflow.Duration = parseValue(line)
 | 
				
			||||||
 | 
							case strings.HasPrefix(line, "Progress:"):
 | 
				
			||||||
 | 
								workflow.Progress = parseValue(line)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &workflow
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseValue(line string) string {
 | 
				
			||||||
 | 
						parts := strings.SplitN(line, ":", 2)
 | 
				
			||||||
 | 
						if len(parts) < 2 {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.TrimSpace(parts[1])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseBoolValue(line string) bool {
 | 
				
			||||||
 | 
						value := parseValue(line)
 | 
				
			||||||
 | 
						return value == "True"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func LogLocalWorkflow(wfName string, pipe io.ReadCloser, wg *sync.WaitGroup) {
 | 
				
			||||||
 | 
						logger = logs.GetLogger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logger.Debug().Msg("created wf_logger")
 | 
				
			||||||
 | 
						wfLogger = logger.With().Str("argo_name", wfName).Str("workflow_id", conf.GetConfig().WorkflowID).Str("workflow_execution_id", conf.GetConfig().ExecutionID).Logger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var current_watch, previous_watch ArgoWatch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						watch_output := make([]string, 0)
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(pipe)
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							log := scanner.Text()
 | 
				
			||||||
 | 
							watch_output = append(watch_output, log)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Log the progress of the WF
 | 
				
			||||||
 | 
							if strings.HasPrefix(log, "Progress:") {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								current_watch = *NewLocalArgoWatch(watch_output)
 | 
				
			||||||
 | 
								workflowName := current_watch.Name
 | 
				
			||||||
 | 
								if !current_watch.Equals(&previous_watch) {
 | 
				
			||||||
 | 
									wg.Add(1)
 | 
				
			||||||
 | 
									// checkStatus(current_watch.Status, previous_watch.Status)
 | 
				
			||||||
 | 
									jsonified, err := json.Marshal(current_watch)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										logger.Error().Msg("Could not create watch log for " + workflowName)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									wfLogger.Info().Msg(string(jsonified))
 | 
				
			||||||
 | 
									previous_watch = current_watch
 | 
				
			||||||
 | 
									current_watch = ArgoWatch{}
 | 
				
			||||||
 | 
									wg.Done()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Debug, no logs sent
 | 
				
			||||||
 | 
					func LogLocalPod(wfName string, pipe io.ReadCloser, steps []string, wg *sync.WaitGroup) {
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(pipe)
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							var podLogger zerolog.Logger
 | 
				
			||||||
 | 
							wg.Add(1)
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							line := scanner.Text()
 | 
				
			||||||
 | 
							podName := strings.Split(line, ":")[0]
 | 
				
			||||||
 | 
							podLogger = wfLogger.With().Str("step_name", getStepName(podName, steps)).Logger()
 | 
				
			||||||
 | 
							log := strings.Split(line,podName+":")[1]
 | 
				
			||||||
 | 
							podLog := NewArgoPodLog(wfName,podName,log)
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							jsonifiedLog, err := json.Marshal(podLog)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								podLogger.Fatal().Msg(err.Error())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							podLogger.Info().Msg(string(jsonifiedLog))
 | 
				
			||||||
 | 
							wg.Done()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getStepName(podName string, steps []string) string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, step := range(steps) {
 | 
				
			||||||
 | 
							if strings.Contains(podName,step){
 | 
				
			||||||
 | 
								return step
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return "error"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										220
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										220
									
								
								main.go
									
									
									
									
									
								
							@@ -3,18 +3,17 @@ package main
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"slices"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"oc-monitord/conf"
 | 
						"oc-monitord/conf"
 | 
				
			||||||
	"oc-monitord/models"
 | 
						l "oc-monitord/logger"
 | 
				
			||||||
	u "oc-monitord/utils"
 | 
						u "oc-monitord/utils"
 | 
				
			||||||
	"oc-monitord/workflow_builder"
 | 
						"oc-monitord/workflow_builder"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,14 +64,17 @@ func main() {
 | 
				
			|||||||
		conf.GetConfig().Logs,
 | 
							conf.GetConfig().Logs,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logger = logs.CreateLogger("oc-monitord")
 | 
						logger = u.GetLogger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logger.Debug().Msg("Loki URL : " + conf.GetConfig().LokiURL)
 | 
						logger.Debug().Msg("Loki URL : " + conf.GetConfig().LokiURL)
 | 
				
			||||||
	logger.Debug().Msg("Workflow executed : " + conf.GetConfig().ExecutionID)
 | 
						logger.Info().Msg("Workflow executed : " + conf.GetConfig().ExecutionID)
 | 
				
			||||||
	exec := u.GetExecution(conf.GetConfig().ExecutionID)
 | 
						exec := u.GetExecution(conf.GetConfig().ExecutionID)
 | 
				
			||||||
 | 
						if exec == nil {
 | 
				
			||||||
 | 
							logger.Fatal().Msg("Could not retrieve workflow ID from execution ID " + conf.GetConfig().ExecutionID + " on peer " + conf.GetConfig().PeerID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	conf.GetConfig().WorkflowID = exec.WorkflowID
 | 
						conf.GetConfig().WorkflowID = exec.WorkflowID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logger.Debug().Msg("Starting construction of yaml argo for workflow :" + exec.WorkflowID)
 | 
						logger.Info().Msg("Starting construction of yaml argo for workflow :" + exec.WorkflowID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err := os.Stat("./argo_workflows/"); os.IsNotExist(err) {
 | 
						if _, err := os.Stat("./argo_workflows/"); os.IsNotExist(err) {
 | 
				
			||||||
		os.Mkdir("./argo_workflows/", 0755)
 | 
							os.Mkdir("./argo_workflows/", 0755)
 | 
				
			||||||
@@ -84,146 +86,121 @@ func main() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	err := new_wf.LoadFrom(conf.GetConfig().WorkflowID, conf.GetConfig().PeerID)
 | 
						err := new_wf.LoadFrom(conf.GetConfig().WorkflowID, conf.GetConfig().PeerID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		logger.Error().Msg("Could not retrieve workflow " + conf.GetConfig().WorkflowID + " from oc-catalog API")
 | 
							logger.Error().Msg("Could not retrieve workflow " + conf.GetConfig().WorkflowID + " from oc-catalog API")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	builder, argo_file_path, stepMax, err := new_wf.ExportToArgo(exec.ExecutionsID, conf.GetConfig().Timeout)
 | 
						builder, _, err := new_wf.ExportToArgo(exec.ExecutionsID, conf.GetConfig().Timeout) // Removed stepMax so far, I don't know if we need it anymore
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logger.Error().Msg("Could not create the Argo file for " + conf.GetConfig().WorkflowID)
 | 
							logger.Error().Msg("Could not create the Argo file for " + conf.GetConfig().WorkflowID)
 | 
				
			||||||
		logger.Error().Msg(err.Error())
 | 
							logger.Error().Msg(err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	logger.Debug().Msg("Created :" + argo_file_path)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	workflowName = getContainerName(argo_file_path)
 | 
						argoFilePath, err := builder.CompleteBuild(exec.ExecutionsID)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	wf_logger = logger.With().Str("argo_name", workflowName).Str("workflow_id", conf.GetConfig().WorkflowID).Str("workflow_execution_id", conf.GetConfig().ExecutionID).Logger()
 | 
					 | 
				
			||||||
	wf_logger.Debug().Msg("Testing argo name")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = builder.CompleteBuild(exec.ExecutionsID)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logger.Error().Msg(err.Error())
 | 
							logger.Error().Msg("Error when completing the build of the workflow: " + err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						workflowName = getContainerName(argoFilePath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if conf.GetConfig().KubeHost == "" {
 | 
				
			||||||
 | 
							// Not in a k8s environment, get conf from parameters
 | 
				
			||||||
 | 
							logger.Info().Msg("Executes outside of k8s")
 | 
				
			||||||
 | 
							executeOutside(argoFilePath, builder.Workflow)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Executed in a k8s environment
 | 
				
			||||||
 | 
							logger.Info().Msg("Executes inside a k8s")
 | 
				
			||||||
 | 
							// executeInside(exec.GetID(), "argo", argo_file_path, stepMax)  // commenting to use conf.ExecutionID instead of exec.GetID()
 | 
				
			||||||
 | 
							executeInside(conf.GetConfig().ExecutionID, exec.ExecutionsID, argoFilePath)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_ = stepMax
 | 
					 | 
				
			||||||
	// if conf.GetConfig().KubeHost == "" {
 | 
					 | 
				
			||||||
	// 	// Not in a k8s environment, get conf from parameters
 | 
					 | 
				
			||||||
	// 	fmt.Println("Executes outside of k8s")
 | 
					 | 
				
			||||||
	// 	executeOutside(argo_file_path, stepMax)
 | 
					 | 
				
			||||||
	// } else {
 | 
					 | 
				
			||||||
	// 	// Executed in a k8s environment
 | 
					 | 
				
			||||||
	// 	fmt.Println("Executes inside a k8s")
 | 
					 | 
				
			||||||
	// 	executeInside(exec.GetID(), "argo", argo_file_path, stepMax)
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// So far we only log the output from
 | 
					// So far we only log the output from
 | 
				
			||||||
func executeInside(execID string, ns string, argo_file_path string, stepMax int) {
 | 
					func executeInside(execID string, ns string, argo_file_path string) {
 | 
				
			||||||
	t, err := tools2.NewService(conf.GetConfig().Mode)
 | 
						t, err := tools2.NewService(conf.GetConfig().Mode)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logger.Error().Msg("Could not create KubernetesTool")
 | 
							logger.Error().Msg("Could not create KubernetesTool")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	name, err := t.CreateArgoWorkflow(argo_file_path, ns)
 | 
						name, err := t.CreateArgoWorkflow(argo_file_path, ns)
 | 
				
			||||||
 | 
						// _ = name 
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logger.Error().Msg("Could not create argo workflow : " + err.Error())
 | 
							logger.Error().Msg("Could not create argo workflow : " + err.Error())
 | 
				
			||||||
 | 
							logger.Info().Msg(fmt.Sprint("CA :" + conf.GetConfig().KubeCA))
 | 
				
			||||||
 | 
							logger.Info().Msg(fmt.Sprint("Cert :" + conf.GetConfig().KubeCert))
 | 
				
			||||||
 | 
							logger.Info().Msg(fmt.Sprint("Data :" + conf.GetConfig().KubeData))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		split := strings.Split(argo_file_path, "_")
 | 
							watcher, err := t.GetArgoWatch(ns, workflowName)
 | 
				
			||||||
		argoLogs := models.NewArgoLogs(split[0], "argo", stepMax)
 | 
							if err != nil {
 | 
				
			||||||
		argoLogs.StartStepRecording(argoLogs.NewWatch(), wf_logger)
 | 
								logger.Error().Msg("Could not retrieve Watcher : " + err.Error())
 | 
				
			||||||
		err := t.LogWorkflow(execID, ns, name, argo_file_path, stepMax, argoLogs.NewWatch(), argoLogs.NewWatch(), argoLogs, []string{}, logWorkflow)
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							l.LogKubernetesArgo(name, ns, watcher)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			logger.Error().Msg("Could not log workflow : " + err.Error())
 | 
								logger.Error().Msg("Could not log workflow : " + err.Error())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							logger.Info().Msg("Finished, exiting...")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func executeOutside(argo_file_path string, stepMax int) {
 | 
					func executeOutside(argo_file_path string, workflow workflow_builder.Workflow) {
 | 
				
			||||||
	// var stdout, stderr, stdout_logs, stderr_logs 	io.ReadCloser
 | 
						var stdoutSubmit, stderrSubmit io.ReadCloser
 | 
				
			||||||
	var stdout, stderr io.ReadCloser
 | 
						var stdoutLogs, stderrLogs io.ReadCloser
 | 
				
			||||||
	// var stderr 	io.ReadCloser
 | 
						var wg sync.WaitGroup
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	cmd := exec.Command("argo", "submit", "--log", argo_file_path, "--serviceaccount=argo", "-n", "argo")
 | 
					
 | 
				
			||||||
	if stdout, err = cmd.StdoutPipe(); err != nil {
 | 
						logger.Debug().Msg("executing :" + "argo submit --watch " + argo_file_path + " --serviceaccount sa-" + conf.GetConfig().ExecutionID + " -n " + conf.GetConfig().ExecutionID )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmdSubmit := exec.Command("argo", "submit", "--watch", argo_file_path, "--serviceaccount", "sa-"+conf.GetConfig().ExecutionID, "-n", conf.GetConfig().ExecutionID)
 | 
				
			||||||
 | 
						if stdoutSubmit, err = cmdSubmit.StdoutPipe(); err != nil {
 | 
				
			||||||
		wf_logger.Error().Msg("Could not retrieve stdoutpipe " + err.Error())
 | 
							wf_logger.Error().Msg("Could not retrieve stdoutpipe " + err.Error())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := cmd.Start(); err != nil {
 | 
					 | 
				
			||||||
		panic(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var wg sync.WaitGroup
 | 
					 | 
				
			||||||
	split := strings.Split(argo_file_path, "_")
 | 
					 | 
				
			||||||
	argoLogs := models.NewArgoLogs(split[0], "argo", stepMax)
 | 
					 | 
				
			||||||
	argoLogs.StartStepRecording(argoLogs.NewWatch(), wf_logger)
 | 
					 | 
				
			||||||
	argoLogs.IsStreaming = true
 | 
					 | 
				
			||||||
	go logWorkflow(argo_file_path, stepMax, stdout, argoLogs.NewWatch(), argoLogs.NewWatch(), argoLogs, []string{}, &wg)
 | 
					 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	if err := cmd.Wait(); err != nil {
 | 
						cmdLogs := exec.Command("argo", "logs", "oc-monitor-"+workflowName, "-n", conf.GetConfig().ExecutionID, "--follow","--no-color")
 | 
				
			||||||
		wf_logger.Error().Msg("Could not execute argo submit")
 | 
						if stdoutLogs, err = cmdLogs.StdoutPipe(); err != nil {
 | 
				
			||||||
		wf_logger.Error().Msg(err.Error() + bufio.NewScanner(stderr).Text())
 | 
							wf_logger.Error().Msg("Could not retrieve stdoutpipe for 'argo logs'" + err.Error())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						var steps []string
 | 
				
			||||||
 | 
						for _, template := range workflow.Spec.Templates {
 | 
				
			||||||
 | 
							steps = append(steps, template.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go l.LogLocalWorkflow(workflowName, stdoutSubmit, &wg)
 | 
				
			||||||
 | 
						go l.LogLocalPod(workflowName, stdoutLogs, steps, &wg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logger.Info().Msg("Starting argo submit")
 | 
				
			||||||
 | 
						if err := cmdSubmit.Start(); err != nil {
 | 
				
			||||||
 | 
							wf_logger.Error().Msg("Could not start argo submit")
 | 
				
			||||||
 | 
							wf_logger.Error().Msg(err.Error() + bufio.NewScanner(stderrSubmit).Text())
 | 
				
			||||||
		updateStatus("fatal", "")
 | 
							updateStatus("fatal", "")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						time.Sleep(5 * time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logger.Info().Msg("Running argo logs")
 | 
				
			||||||
 | 
						if err := cmdLogs.Run(); err != nil {
 | 
				
			||||||
 | 
							wf_logger.Error().Msg("Could not run '" + strings.Join(cmdLogs.Args, " ") + "'")
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							wf_logger.Fatal().Msg(err.Error() + bufio.NewScanner(stderrLogs).Text())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logger.Info().Msg("Waiting argo submit")
 | 
				
			||||||
 | 
						if err := cmdSubmit.Wait(); err != nil {
 | 
				
			||||||
 | 
							wf_logger.Error().Msg("Could not execute argo submit")
 | 
				
			||||||
 | 
							wf_logger.Error().Msg(err.Error() + bufio.NewScanner(stderrSubmit).Text())
 | 
				
			||||||
 | 
							updateStatus("fatal", "")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wg.Wait()
 | 
						wg.Wait()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// We could improve this function by creating an object with the same attribute as the output
 | 
					 | 
				
			||||||
// and only send a new log if the current object has different values than the previous
 | 
					 | 
				
			||||||
func logWorkflow(argo_file_path string, stepMax int, pipe io.ReadCloser,
 | 
					 | 
				
			||||||
	current_watch *models.ArgoWatch, previous_watch *models.ArgoWatch,
 | 
					 | 
				
			||||||
	argoLogs *models.ArgoLogs, seen []string, wg *sync.WaitGroup) {
 | 
					 | 
				
			||||||
	scanner := bufio.NewScanner(pipe)
 | 
					 | 
				
			||||||
	count := 0
 | 
					 | 
				
			||||||
	see := ""
 | 
					 | 
				
			||||||
	seeit := 0
 | 
					 | 
				
			||||||
	for scanner.Scan() {
 | 
					 | 
				
			||||||
		log := scanner.Text()
 | 
					 | 
				
			||||||
		if strings.Contains(log, "capturing logs") && count == 0 {
 | 
					 | 
				
			||||||
			if !argoLogs.IsStreaming {
 | 
					 | 
				
			||||||
				wg.Add(1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			seeit++
 | 
					 | 
				
			||||||
		} else if count == 0 {
 | 
					 | 
				
			||||||
			if argoLogs.IsStreaming {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if count == 1 {
 | 
					 | 
				
			||||||
			see = log
 | 
					 | 
				
			||||||
			if slices.Contains(argoLogs.Seen, see) && !argoLogs.IsStreaming {
 | 
					 | 
				
			||||||
				wg.Done()
 | 
					 | 
				
			||||||
				seeit--
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !slices.Contains(current_watch.Logs, log) {
 | 
					 | 
				
			||||||
			current_watch.Logs = append(current_watch.Logs, strings.ReplaceAll(log, "\"", ""))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		count++
 | 
					 | 
				
			||||||
		if strings.Contains(log, "sub-process exited") {
 | 
					 | 
				
			||||||
			current_watch = argoLogs.StopStepRecording(current_watch)
 | 
					 | 
				
			||||||
			argoLogs.Seen = append(argoLogs.Seen, see)
 | 
					 | 
				
			||||||
			if checkStatus(current_watch, previous_watch, argoLogs) {
 | 
					 | 
				
			||||||
				count = 0
 | 
					 | 
				
			||||||
				if !argoLogs.IsStreaming {
 | 
					 | 
				
			||||||
					wg.Done()
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				seeit--
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			jsonified, err := json.Marshal(current_watch)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				logger.Error().Msg("Could not create watch log")
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if current_watch.Status == "Failed" {
 | 
					 | 
				
			||||||
				wf_logger.Error().Msg(string(jsonified))
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				wf_logger.Info().Msg(string(jsonified))
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			previous_watch = current_watch
 | 
					 | 
				
			||||||
			current_watch = &models.ArgoWatch{}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func loadConfig(is_k8s bool, parser *argparse.Parser) {
 | 
					func loadConfig(is_k8s bool, parser *argparse.Parser) {
 | 
				
			||||||
	var o *onion.Onion
 | 
						var o *onion.Onion
 | 
				
			||||||
@@ -251,9 +228,11 @@ func setConf(is_k8s bool, o *onion.Onion, parser *argparse.Parser) {
 | 
				
			|||||||
	host := parser.String("H", "host", &argparse.Options{Required: false, Default: "", Help: "Host for the Kubernetes cluster"})
 | 
						host := parser.String("H", "host", &argparse.Options{Required: false, Default: "", Help: "Host for the Kubernetes cluster"})
 | 
				
			||||||
	port := parser.String("P", "port", &argparse.Options{Required: false, Default: "6443", Help: "Port for the Kubernetes cluster"})
 | 
						port := parser.String("P", "port", &argparse.Options{Required: false, Default: "6443", Help: "Port for the Kubernetes cluster"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// argoHost := parser.String("h", "argoHost", &argparse.Options{Required: false, Default: "", Help: "Host where Argo is running from"}) // can't use -h because its reserved to help
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := parser.Parse(os.Args)
 | 
						err := parser.Parse(os.Args)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println(parser.Usage(err))
 | 
							logger.Info().Msg(parser.Usage(err))
 | 
				
			||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	conf.GetConfig().Logs = "debug"
 | 
						conf.GetConfig().Logs = "debug"
 | 
				
			||||||
@@ -268,6 +247,8 @@ func setConf(is_k8s bool, o *onion.Onion, parser *argparse.Parser) {
 | 
				
			|||||||
	conf.GetConfig().KubeHost = *host
 | 
						conf.GetConfig().KubeHost = *host
 | 
				
			||||||
	conf.GetConfig().KubePort = *port
 | 
						conf.GetConfig().KubePort = *port
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// conf.GetConfig().ArgoHost = *argoHost
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	decoded, err := base64.StdEncoding.DecodeString(*ca)
 | 
						decoded, err := base64.StdEncoding.DecodeString(*ca)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		conf.GetConfig().KubeCA = string(decoded)
 | 
							conf.GetConfig().KubeCA = string(decoded)
 | 
				
			||||||
@@ -320,29 +301,10 @@ func getContainerName(argo_file string) string {
 | 
				
			|||||||
	re := regexp.MustCompile(regex)
 | 
						re := regexp.MustCompile(regex)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	container_name := re.FindString(argo_file)
 | 
						container_name := re.FindString(argo_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return container_name
 | 
						return container_name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Uses the ArgoWatch object to update status of the workflow execution object
 | 
					 | 
				
			||||||
func checkStatus(current *models.ArgoWatch, previous *models.ArgoWatch, argoLogs *models.ArgoLogs) bool {
 | 
					 | 
				
			||||||
	if previous == nil || current.Status != previous.Status || argoLogs.IsStreaming {
 | 
					 | 
				
			||||||
		argoLogs.StepCount += 1
 | 
					 | 
				
			||||||
		if len(current.Logs) > 0 {
 | 
					 | 
				
			||||||
			newLogs := []string{}
 | 
					 | 
				
			||||||
			for _, log := range current.Logs {
 | 
					 | 
				
			||||||
				if !slices.Contains(argoLogs.Logs, log) {
 | 
					 | 
				
			||||||
					newLogs = append(newLogs, log)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			updateStatus(current.Status, strings.Join(newLogs, "\n"))
 | 
					 | 
				
			||||||
			current.Logs = newLogs
 | 
					 | 
				
			||||||
			argoLogs.Logs = append(argoLogs.Logs, newLogs...)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			updateStatus(current.Status, "")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return previous == nil || current.Status != previous.Status || argoLogs.IsStreaming
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func updateStatus(status string, log string) {
 | 
					func updateStatus(status string, log string) {
 | 
				
			||||||
	exec_id := conf.GetConfig().ExecutionID
 | 
						exec_id := conf.GetConfig().ExecutionID
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										66
									
								
								minio-doc/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								minio-doc/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					# Goal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We want to be able to instantiate a service that allows to store file located on a `processing` pod onto it. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We have already tested it with a static `Argo` yaml file, a MinIO running on the same kubernetes node, the minio service is reached because it is the only associated to the `serviceAccount`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We have established three otpions that need to be available to the user for the feature to be implemented: 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Use a MinIO running constantly on the node that executes the argo workflow
 | 
				
			||||||
 | 
					- Use a MinIO
 | 
				
			||||||
 | 
					- A MinIO is instanciated when a new workflow is launched
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Requirements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Helm : `https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3`
 | 
				
			||||||
 | 
					- Helm GO client : `$ go get github.com/mittwald/go-helm-client`
 | 
				
			||||||
 | 
					- MinIO chart : `https://charts.min.io/`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ressources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We need to create several ressources in order for the pods to communicate with the MinIO 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## MinIO Auth Secrets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bucket ConfigMap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With the name `artifact-repositories` this configMap will be used by default. It contains the URL to the MinIO server and the key to the authentication data held in a `secret` ressource. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					apiVersion: v1
 | 
				
			||||||
 | 
					kind: ConfigMap
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  # If you want to use this config map by default, name it "artifact-repositories".
 | 
				
			||||||
 | 
					  name: artifact-repositories
 | 
				
			||||||
 | 
					  # annotations:
 | 
				
			||||||
 | 
					  #   # v3.0 and after - if you want to use a specific key, put that key into this annotation.
 | 
				
			||||||
 | 
					  #   workflows.argoproj.io/default-artifact-repository: oc-s3-artifact-repository
 | 
				
			||||||
 | 
					data:
 | 
				
			||||||
 | 
					  oc-s3-artifact-repository: |
 | 
				
			||||||
 | 
					    s3:
 | 
				
			||||||
 | 
					      bucket: oc-bucket
 | 
				
			||||||
 | 
					      endpoint: [ retrieve cluster with kubectl get service argo-artifacts -o jsonpath="{.spec.clusterIP}" ]:9000
 | 
				
			||||||
 | 
					      insecure: true
 | 
				
			||||||
 | 
					      accessKeySecret: 
 | 
				
			||||||
 | 
					        name: argo-artifact-secret
 | 
				
			||||||
 | 
					        key: access-key
 | 
				
			||||||
 | 
					      secretKeySecret:
 | 
				
			||||||
 | 
					        name: argo-artifact-secret
 | 
				
			||||||
 | 
					        key: secret-key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Code modifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Rajouter un attribut "isDataLink" 
 | 
				
			||||||
 | 
					 - true/false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Rajouter un attribut DataPath ou un truc comme ca
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - liste de map[string]string permet de n'avoir qu'une copie par fichier)
 | 
				
			||||||
 | 
					  - éditable uniquement a travers la méthode addDataPath
 | 
				
			||||||
 | 
					  - clé : path du fichier / value : nom de la copie dans minio	
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					===> on a besoin du meme attribut pour Processing -> Data et Data -> Processing
 | 
				
			||||||
@@ -1,145 +0,0 @@
 | 
				
			|||||||
package models
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/acarl005/stripansi"
 | 
					 | 
				
			||||||
	"github.com/rs/zerolog"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ArgoWatch struct {
 | 
					 | 
				
			||||||
	Name      string
 | 
					 | 
				
			||||||
	Namespace string
 | 
					 | 
				
			||||||
	Status    string
 | 
					 | 
				
			||||||
	Conditions
 | 
					 | 
				
			||||||
	Created  string
 | 
					 | 
				
			||||||
	Started  string
 | 
					 | 
				
			||||||
	Duration string
 | 
					 | 
				
			||||||
	Progress string
 | 
					 | 
				
			||||||
	Logs     []string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Conditions struct {
 | 
					 | 
				
			||||||
	PodRunning bool
 | 
					 | 
				
			||||||
	Completed  bool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (a *ArgoWatch) Equals(arg *ArgoWatch) bool {
 | 
					 | 
				
			||||||
	if arg == nil {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return a.Status == arg.Status && a.Progress == arg.Progress && a.Conditions.PodRunning == arg.Conditions.PodRunning && a.Conditions.Completed == arg.Conditions.Completed
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func NewArgoLogs(name string, namespace string, stepMax int) *ArgoLogs {
 | 
					 | 
				
			||||||
	return &ArgoLogs{
 | 
					 | 
				
			||||||
		Name:        "oc-monitor-" + name,
 | 
					 | 
				
			||||||
		Namespace:   namespace,
 | 
					 | 
				
			||||||
		CreatedDate: time.Now().Format("2006-01-02 15:04:05"),
 | 
					 | 
				
			||||||
		StepCount:   0,
 | 
					 | 
				
			||||||
		StepMax:     stepMax,
 | 
					 | 
				
			||||||
		stop:        false,
 | 
					 | 
				
			||||||
		Seen:        []string{},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ArgoLogs struct {
 | 
					 | 
				
			||||||
	Name        string
 | 
					 | 
				
			||||||
	Namespace   string
 | 
					 | 
				
			||||||
	CreatedDate string
 | 
					 | 
				
			||||||
	StepCount   int
 | 
					 | 
				
			||||||
	StepMax     int
 | 
					 | 
				
			||||||
	stop        bool
 | 
					 | 
				
			||||||
	Started     time.Time
 | 
					 | 
				
			||||||
	Seen        []string
 | 
					 | 
				
			||||||
	Logs        []string
 | 
					 | 
				
			||||||
	IsStreaming bool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (a *ArgoLogs) NewWatch() *ArgoWatch {
 | 
					 | 
				
			||||||
	return &ArgoWatch{
 | 
					 | 
				
			||||||
		Name:      a.Name,
 | 
					 | 
				
			||||||
		Namespace: a.Namespace,
 | 
					 | 
				
			||||||
		Status:    "Pending",
 | 
					 | 
				
			||||||
		Created:   a.CreatedDate,
 | 
					 | 
				
			||||||
		Started:   a.Started.Format("2006-01-02 15:04:05"),
 | 
					 | 
				
			||||||
		Conditions: Conditions{
 | 
					 | 
				
			||||||
			PodRunning: a.StepCount > 0 && a.StepCount < a.StepMax,
 | 
					 | 
				
			||||||
			Completed:  a.StepCount == a.StepMax,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Progress: fmt.Sprintf("%v/%v", a.StepCount, a.StepMax),
 | 
					 | 
				
			||||||
		Duration: "0s",
 | 
					 | 
				
			||||||
		Logs:     []string{},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (a *ArgoLogs) StartStepRecording(current_watch *ArgoWatch, logger zerolog.Logger) {
 | 
					 | 
				
			||||||
	jsonified, _ := json.Marshal(current_watch)
 | 
					 | 
				
			||||||
	logger.Info().Msg(string(jsonified))
 | 
					 | 
				
			||||||
	a.StepCount += 1
 | 
					 | 
				
			||||||
	a.Started = time.Now()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (a *ArgoLogs) StopStepRecording(current *ArgoWatch) *ArgoWatch {
 | 
					 | 
				
			||||||
	fn := strings.Split(a.Name, "_")
 | 
					 | 
				
			||||||
	logs := []string{}
 | 
					 | 
				
			||||||
	err := false
 | 
					 | 
				
			||||||
	end := ""
 | 
					 | 
				
			||||||
	for _, input := range current.Logs {
 | 
					 | 
				
			||||||
		line := strings.TrimSpace(input)
 | 
					 | 
				
			||||||
		if line == "" || !strings.Contains(line, fn[0]) || !strings.Contains(line, ":") {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		step := strings.Split(line, ":")
 | 
					 | 
				
			||||||
		if strings.Contains(line, "sub-process exited") {
 | 
					 | 
				
			||||||
			b := strings.Split(line, "time=\"")
 | 
					 | 
				
			||||||
			if len(b) > 1 {
 | 
					 | 
				
			||||||
				end = b[1][:19]
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if len(step) < 2 || strings.Contains(line, "time=") || strings.TrimSpace(strings.Join(step[1:], " : ")) == "" || strings.TrimSpace(strings.Join(step[1:], " : ")) == a.Name {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		log := stripansi.Strip(strings.TrimSpace(strings.Join(step[1:], " : ")))
 | 
					 | 
				
			||||||
		t, e := strconv.Unquote(log)
 | 
					 | 
				
			||||||
		if e == nil {
 | 
					 | 
				
			||||||
			logs = append(logs, t)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			logs = append(logs, strings.ReplaceAll(log, "\"", "`"))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if strings.Contains(logs[len(logs)-1], "Error") {
 | 
					 | 
				
			||||||
			err = true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	status := "Pending"
 | 
					 | 
				
			||||||
	if a.StepCount > 0 {
 | 
					 | 
				
			||||||
		status = "Running"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if a.StepCount == a.StepMax {
 | 
					 | 
				
			||||||
		if err {
 | 
					 | 
				
			||||||
			status = "Failed"
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			status = "Succeeded"
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	duration := float64(0)
 | 
					 | 
				
			||||||
	if end != "" {
 | 
					 | 
				
			||||||
		timeE, _ := time.Parse("2006-01-02T15:04:05", end)
 | 
					 | 
				
			||||||
		duration = timeE.Sub(a.Started).Seconds()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	current.Conditions = Conditions{
 | 
					 | 
				
			||||||
		PodRunning: a.StepCount > 0 && a.StepCount < a.StepMax,
 | 
					 | 
				
			||||||
		Completed:  a.StepCount == a.StepMax,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	current.Progress = fmt.Sprintf("%v/%v", a.StepCount, a.StepMax)
 | 
					 | 
				
			||||||
	current.Duration = fmt.Sprintf("%v", fmt.Sprintf("%.2f", duration)+"s")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	current.Status = status
 | 
					 | 
				
			||||||
	return current
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -58,6 +58,7 @@ type Dag struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type TemplateMetadata struct {
 | 
					type TemplateMetadata struct {
 | 
				
			||||||
	Labels map[string]string `yaml:"labels,omitempty"`
 | 
						Labels map[string]string `yaml:"labels,omitempty"`
 | 
				
			||||||
 | 
						Annotations map[string]string `yaml:"annotations,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Secret struct {
 | 
					type Secret struct {
 | 
				
			||||||
@@ -70,8 +71,8 @@ type Key struct {
 | 
				
			|||||||
	Bucket          string  `yaml:"bucket"`
 | 
						Bucket          string  `yaml:"bucket"`
 | 
				
			||||||
	EndPoint        string  `yaml:"endpoint"`
 | 
						EndPoint        string  `yaml:"endpoint"`
 | 
				
			||||||
	Insecure        bool    `yaml:"insecure"`
 | 
						Insecure        bool    `yaml:"insecure"`
 | 
				
			||||||
	AccessKeySecret *Secret `yaml accessKeySecret`
 | 
						AccessKeySecret *Secret `yaml:"accessKeySecret"`
 | 
				
			||||||
	SecretKeySecret *Secret `yaml secretKeySecret`
 | 
						SecretKeySecret *Secret `yaml:"secretKeySecret"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Artifact struct {
 | 
					type Artifact struct {
 | 
				
			||||||
@@ -130,7 +131,7 @@ func (template *Template) CreateContainer(processing *resources.ProcessingResour
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (template *Template) ReplacePerEnv(arg string, envs []models.Param) string {
 | 
					func (template *Template) ReplacePerEnv(arg string, envs []models.Param) string {
 | 
				
			||||||
	for _, v := range envs {
 | 
						for _, v := range envs {
 | 
				
			||||||
		if strings.Contains(arg, v.Name) {
 | 
							if v.Name != "" && strings.Contains(arg, v.Name) {
 | 
				
			||||||
			value := "{{ inputs.parameters." + v.Name + " }}"
 | 
								value := "{{ inputs.parameters." + v.Name + " }}"
 | 
				
			||||||
			arg = strings.ReplaceAll(arg, v.Name, value)
 | 
								arg = strings.ReplaceAll(arg, v.Name, value)
 | 
				
			||||||
			arg = strings.ReplaceAll(arg, "$"+v.Name, value)
 | 
								arg = strings.ReplaceAll(arg, "$"+v.Name, value)
 | 
				
			||||||
@@ -139,3 +140,13 @@ func (template *Template) ReplacePerEnv(arg string, envs []models.Param) string
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return arg
 | 
						return arg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add the metadata that allow Admiralty to pick up an Argo Workflow that needs to be reparted
 | 
				
			||||||
 | 
					// The value of "clustername" is the peerId, which must be replaced by the node name's for this specific execution
 | 
				
			||||||
 | 
					func (t *Template) AddAdmiraltyAnnotations(peerId string){
 | 
				
			||||||
 | 
						if t.Metadata.Annotations == nil {
 | 
				
			||||||
 | 
							t.Metadata.Annotations = make(map[string]string)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.Metadata.Annotations["multicluster.admiralty.io/elect"] = ""
 | 
				
			||||||
 | 
						t.Metadata.Annotations["multicluster.admiralty.io/clustername"] = peerId
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,17 +3,17 @@ package tools
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"oc-monitord/models"
 | 
					
 | 
				
			||||||
	"sync"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/watch"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Tool interface {
 | 
					type Tool interface {
 | 
				
			||||||
	CreateArgoWorkflow(path string, ns string) (string, error)
 | 
						CreateArgoWorkflow(path string, ns string) (string, error)
 | 
				
			||||||
	CreateAccessSecret(ns string, login string, password string) (string, error)
 | 
						CreateAccessSecret(user string, password string, storageId string, namespace string) (string, error)
 | 
				
			||||||
	LogWorkflow(execID string, namespace string, workflowName string, argoFilePath string, stepMax int, current_watch *models.ArgoWatch, previous_watch *models.ArgoWatch,
 | 
						GetArgoWatch(executionId string, wfName string) (watch.Interface, error)
 | 
				
			||||||
		argoLogs *models.ArgoLogs, seen []string,
 | 
						GetPodLogger(ns string, wfName string, podName string) (io.ReadCloser, error)
 | 
				
			||||||
		logFunc func(argoFilePath string, stepMax int, pipe io.ReadCloser, current_watch *models.ArgoWatch, previous_watch *models.ArgoWatch,
 | 
						GetS3Secret(storageId string, namespace string) *v1.Secret
 | 
				
			||||||
			argoLogs *models.ArgoLogs, seen []string, wg *sync.WaitGroup)) error
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _service = map[string]func() (Tool, error){
 | 
					var _service = map[string]func() (Tool, error){
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,26 +2,22 @@ package tools
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"encoding/base64"
 | 
					 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"oc-monitord/conf"
 | 
						"oc-monitord/conf"
 | 
				
			||||||
	"oc-monitord/models"
 | 
					 | 
				
			||||||
	"oc-monitord/utils"
 | 
						"oc-monitord/utils"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"cloud.o-forge.io/core/oc-lib/models/common/enum"
 | 
					 | 
				
			||||||
	wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
 | 
						wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
 | 
				
			||||||
	"github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned"
 | 
						"github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned"
 | 
				
			||||||
	"github.com/google/uuid"
 | 
					 | 
				
			||||||
	corev1 "k8s.io/api/core/v1"
 | 
					 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						k8serrors "k8s.io/apimachinery/pkg/api/errors"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime/serializer"
 | 
						"k8s.io/apimachinery/pkg/runtime/serializer"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/watch"
 | 
				
			||||||
	"k8s.io/client-go/kubernetes"
 | 
						"k8s.io/client-go/kubernetes"
 | 
				
			||||||
	"k8s.io/client-go/rest"
 | 
						"k8s.io/client-go/rest"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -34,7 +30,7 @@ type KubernetesTools struct {
 | 
				
			|||||||
func NewKubernetesTool() (Tool, error) {
 | 
					func NewKubernetesTool() (Tool, error) {
 | 
				
			||||||
	// Load Kubernetes config (from ~/.kube/config)
 | 
						// Load Kubernetes config (from ~/.kube/config)
 | 
				
			||||||
	config := &rest.Config{
 | 
						config := &rest.Config{
 | 
				
			||||||
		Host: conf.GetConfig().KubeHost + ":" + conf.GetConfig().KubePort,
 | 
							Host: "https://" + conf.GetConfig().KubeHost + ":" + conf.GetConfig().KubePort,
 | 
				
			||||||
		TLSClientConfig: rest.TLSClientConfig{
 | 
							TLSClientConfig: rest.TLSClientConfig{
 | 
				
			||||||
			CAData:   []byte(conf.GetConfig().KubeCA),
 | 
								CAData:   []byte(conf.GetConfig().KubeCA),
 | 
				
			||||||
			CertData: []byte(conf.GetConfig().KubeCert),
 | 
								CertData: []byte(conf.GetConfig().KubeCert),
 | 
				
			||||||
@@ -52,76 +48,13 @@ func NewKubernetesTool() (Tool, error) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.New("Error creating Kubernetes versionned client: " + err.Error())
 | 
							return nil, errors.New("Error creating Kubernetes versionned client: " + err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &KubernetesTools{
 | 
						return &KubernetesTools{
 | 
				
			||||||
		Set:          clientset,
 | 
							Set:          clientset,
 | 
				
			||||||
		VersionedSet: clientset2,
 | 
							VersionedSet: clientset2,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (k *KubernetesTools) LogWorkflow(execID string, namespace string, workflowName string, argoFilePath string, stepMax int, current_watch *models.ArgoWatch, previous_watch *models.ArgoWatch, argoLogs *models.ArgoLogs,
 | 
					 | 
				
			||||||
	seen []string, logFunc func(argoFilePath string, stepMax int, pipe io.ReadCloser, current_watch *models.ArgoWatch, previous_watch *models.ArgoWatch, argoLogs *models.ArgoLogs, seen []string, wg *sync.WaitGroup)) error {
 | 
					 | 
				
			||||||
	exec := utils.GetExecution(execID)
 | 
					 | 
				
			||||||
	if exec == nil {
 | 
					 | 
				
			||||||
		return errors.New("Could not retrieve workflow ID from execution ID " + execID)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if exec.State == enum.DRAFT || exec.State == enum.FAILURE || exec.State == enum.SUCCESS {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	k.logWorkflow(namespace, workflowName, argoFilePath, stepMax, current_watch, previous_watch, argoLogs, seen, logFunc)
 | 
					 | 
				
			||||||
	return k.LogWorkflow(execID, namespace, workflowName, argoFilePath, stepMax, current_watch, previous_watch, argoLogs, seen, logFunc)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (k *KubernetesTools) logWorkflow(namespace string, workflowName string, argoFilePath string, stepMax int, current_watch *models.ArgoWatch, previous_watch *models.ArgoWatch, argoLogs *models.ArgoLogs,
 | 
					 | 
				
			||||||
	seen []string,
 | 
					 | 
				
			||||||
	logFunc func(argoFilePath string, stepMax int, pipe io.ReadCloser, current_watch *models.ArgoWatch, previous_watch *models.ArgoWatch, argoLogs *models.ArgoLogs, seen []string, wg *sync.WaitGroup)) error {
 | 
					 | 
				
			||||||
	// List pods related to the Argo workflow
 | 
					 | 
				
			||||||
	labelSelector := fmt.Sprintf("workflows.argoproj.io/workflow=%s", workflowName)
 | 
					 | 
				
			||||||
	for retries := 0; retries < 10; retries++ { // Retry for up to ~20 seconds
 | 
					 | 
				
			||||||
		// List workflow pods
 | 
					 | 
				
			||||||
		wfPods, err := k.Set.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{
 | 
					 | 
				
			||||||
			LabelSelector: labelSelector,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// If we found pods, stream logs
 | 
					 | 
				
			||||||
		if len(wfPods.Items) > 0 {
 | 
					 | 
				
			||||||
			var wg sync.WaitGroup
 | 
					 | 
				
			||||||
			// Stream logs from all matching pods
 | 
					 | 
				
			||||||
			for _, pod := range wfPods.Items {
 | 
					 | 
				
			||||||
				for _, container := range pod.Spec.Containers {
 | 
					 | 
				
			||||||
					wg.Add(1)
 | 
					 | 
				
			||||||
					go k.streamLogs(namespace, pod.Name, container.Name, argoFilePath, stepMax, &wg, current_watch, previous_watch, argoLogs, seen, logFunc)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			wg.Wait()
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		time.Sleep(2 * time.Second) // Wait before retrying
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return errors.New("no pods found for the workflow")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Function to stream logs
 | 
					 | 
				
			||||||
func (k *KubernetesTools) streamLogs(namespace string, podName string, containerName string,
 | 
					 | 
				
			||||||
	argoFilePath string, stepMax int, wg *sync.WaitGroup, current_watch *models.ArgoWatch, previous_watch *models.ArgoWatch, argoLogs *models.ArgoLogs, seen []string,
 | 
					 | 
				
			||||||
	logFunc func(argo_file_path string, stepMax int, pipe io.ReadCloser, current_watch *models.ArgoWatch, previous_watch *models.ArgoWatch, argoLogs *models.ArgoLogs, seen []string, wg *sync.WaitGroup)) {
 | 
					 | 
				
			||||||
	req := k.Set.CoreV1().Pods(namespace).GetLogs(podName, &corev1.PodLogOptions{
 | 
					 | 
				
			||||||
		Container: containerName, // Main container
 | 
					 | 
				
			||||||
		Follow:    true,          // Equivalent to -f flag in kubectl logs
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	defer wg.Done()
 | 
					 | 
				
			||||||
	// Open stream
 | 
					 | 
				
			||||||
	stream, err := req.Stream(context.Background())
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer stream.Close()
 | 
					 | 
				
			||||||
	var internalWg sync.WaitGroup
 | 
					 | 
				
			||||||
	logFunc(argoFilePath, stepMax, stream, current_watch, previous_watch, argoLogs, seen, &internalWg)
 | 
					 | 
				
			||||||
	internalWg.Wait()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (k *KubernetesTools) CreateArgoWorkflow(path string, ns string) (string, error) {
 | 
					func (k *KubernetesTools) CreateArgoWorkflow(path string, ns string) (string, error) {
 | 
				
			||||||
	// Read workflow YAML file
 | 
						// Read workflow YAML file
 | 
				
			||||||
	workflowYAML, err := os.ReadFile(path)
 | 
						workflowYAML, err := os.ReadFile(path)
 | 
				
			||||||
@@ -145,37 +78,127 @@ func (k *KubernetesTools) CreateArgoWorkflow(path string, ns string) (string, er
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create the workflow in the "argo" namespace
 | 
						// Create the workflow in the "argo" namespace
 | 
				
			||||||
	createdWf, err := k.VersionedSet.ArgoprojV1alpha1().Workflows(ns).Create(context.Background(), workflow, metav1.CreateOptions{})
 | 
						createdWf, err := k.VersionedSet.ArgoprojV1alpha1().Workflows(ns).Create(context.TODO(), workflow, metav1.CreateOptions{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", errors.New("failed to create workflow: " + err.Error())
 | 
							return "", errors.New("failed to create workflow: " + err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fmt.Printf("workflow %s created in namespace %s\n", createdWf.Name, "argo")
 | 
						l := utils.GetLogger()
 | 
				
			||||||
 | 
						l.Info().Msg(fmt.Sprintf("workflow %s created in namespace %s\n", createdWf.Name, ns))
 | 
				
			||||||
	return createdWf.Name, nil
 | 
						return createdWf.Name, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (k *KubernetesTools) CreateAccessSecret(ns string, login string, password string) (string, error) {
 | 
					func (k *KubernetesTools) CreateAccessSecret(access string, password string, storageId string, namespace string) (string, error) {
 | 
				
			||||||
	// Namespace where the secret will be created
 | 
						// Namespace where the secret will be created
 | 
				
			||||||
	namespace := "default"
 | 
					 | 
				
			||||||
	// Encode the secret data (Kubernetes requires base64-encoded values)
 | 
						// Encode the secret data (Kubernetes requires base64-encoded values)
 | 
				
			||||||
	secretData := map[string][]byte{
 | 
						secretData := map[string][]byte{
 | 
				
			||||||
		"access-key": []byte(base64.StdEncoding.EncodeToString([]byte(login))),
 | 
							"access-key": []byte(access),
 | 
				
			||||||
		"secret-key": []byte(base64.StdEncoding.EncodeToString([]byte(password))),
 | 
							"secret-key": []byte(password),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Define the Secret object
 | 
						// Define the Secret object
 | 
				
			||||||
	name := uuid.New().String()
 | 
						name := storageId+"-secret-s3"
 | 
				
			||||||
	secret := &v1.Secret{
 | 
						secret := &v1.Secret{
 | 
				
			||||||
		ObjectMeta: metav1.ObjectMeta{
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
			Name:      name,
 | 
								Name:      name,
 | 
				
			||||||
			Namespace: ns,
 | 
								Namespace: namespace,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Type: v1.SecretTypeOpaque,
 | 
							Type: v1.SecretTypeOpaque,
 | 
				
			||||||
		Data: secretData,
 | 
							Data: secretData,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Create the Secret in Kubernetes
 | 
						// Create the Secret in Kubernetes
 | 
				
			||||||
	_, err := k.Set.CoreV1().Secrets(namespace).Create(context.Background(), secret, metav1.CreateOptions{})
 | 
						_, err := k.Set.CoreV1().Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", errors.New("Error creating secret: " + err.Error())
 | 
							return "", errors.New("Error creating secret: " + err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return name, nil
 | 
						return name, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *KubernetesTools) GetS3Secret(storageId string, namespace string) *v1.Secret {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secret, err := k.Set.CoreV1().Secrets(namespace).Get(context.TODO(), storageId + "-secret-s3", metav1.GetOptions{})
 | 
				
			||||||
 | 
						// Get(context.TODO(),storageId + "-artifact-server", metav1.GetOptions{})
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if err != nil && !k8serrors.IsNotFound(err) {
 | 
				
			||||||
 | 
							l := utils.GetLogger()
 | 
				
			||||||
 | 
							l.Fatal().Msg("An error happened when retrieving secret in " + namespace + " : " + err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if k8serrors.IsNotFound(err) {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return secret
 | 
				
			||||||
 | 
						// return secret 
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *KubernetesTools) GetArgoWatch(executionId string, wfName string) (watch.Interface, error){
 | 
				
			||||||
 | 
						options := metav1.ListOptions{FieldSelector: "metadata.name=oc-monitor-"+wfName}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						watcher, err := k.VersionedSet.ArgoprojV1alpha1().Workflows(executionId).Watch(context.Background(), options)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, errors.New("Error executing 'argo watch " + wfName + " -n " + executionId + " with ArgoprojV1alpha1 client")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return watcher, nil 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *KubernetesTools) GetPodLogger(ns string, wfName string, nodeName string) (io.ReadCloser, error) {
 | 
				
			||||||
 | 
						var targetPod v1.Pod
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						pods, err := k.Set.CoreV1().Pods(ns).List(context.Background(), metav1.ListOptions{
 | 
				
			||||||
 | 
							LabelSelector: "workflows.argoproj.io/workflow="+wfName,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to list pods: " + err.Error())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if len(pods.Items) == 0 {
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("no pods found with label workflows.argoproj.io/workflow="+ wfName + " no pods found with label workflows.argoproj.io/node-name=" + nodeName + " in namespace " + ns)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					    for _, pod := range pods.Items {
 | 
				
			||||||
 | 
							if pod.Annotations["workflows.argoproj.io/node-name"] == nodeName {
 | 
				
			||||||
 | 
								targetPod = pod
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// k8s API throws an error if we try getting logs while the container are not initialized, so we repeat status check there
 | 
				
			||||||
 | 
						k.testPodReady(targetPod, ns)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// When using kubec	logs for a pod we see it contacts /api/v1/namespaces/NAMESPACE/pods/oc-monitor-PODNAME/log?container=main so we add this container: main to the call
 | 
				
			||||||
 | 
						req, err := k.Set.CoreV1().Pods(ns).GetLogs(targetPod.Name, &v1.PodLogOptions{Follow: true, Container: "main"}). Stream(context.Background())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf(" Error when trying to get logs for " + targetPod.Name + " : " + err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return req, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (k *KubernetesTools) testPodReady(pod v1.Pod, ns string) {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							pod, err := k.Set.CoreV1().Pods(ns).Get(context.Background(), pod.Name, metav1.GetOptions{})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								wfl := utils.GetWFLogger("")
 | 
				
			||||||
 | 
								wfl.Error().Msg("Error fetching pod: " + err.Error() + "\n")
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
							var initialized bool
 | 
				
			||||||
 | 
							for _, cond := range pod.Status.Conditions {
 | 
				
			||||||
 | 
								// It seems that for remote pods the pod gets the Succeeded status before it has time to display the it is ready to run in .status.conditions,so we added the OR condition
 | 
				
			||||||
 | 
								if (cond.Type == v1.PodReady && cond.Status == v1.ConditionTrue) || pod.Status.Phase == v1.PodSucceeded {		
 | 
				
			||||||
 | 
									initialized = true
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
							if initialized {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
							time.Sleep(2 * time.Second) // avoid hammering the API
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,17 +2,46 @@ package utils
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"oc-monitord/conf"
 | 
						"oc-monitord/conf"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	oclib "cloud.o-forge.io/core/oc-lib"
 | 
						oclib "cloud.o-forge.io/core/oc-lib"
 | 
				
			||||||
 | 
						"cloud.o-forge.io/core/oc-lib/logs"
 | 
				
			||||||
	"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
 | 
						"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
 | 
				
			||||||
 | 
						"github.com/rs/zerolog"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						logger 		zerolog.Logger
 | 
				
			||||||
 | 
						wf_logger 	zerolog.Logger
 | 
				
			||||||
 | 
					 	pods_logger zerolog.Logger
 | 
				
			||||||
 | 
						onceLogger 	sync.Once
 | 
				
			||||||
 | 
						onceWF 		sync.Once
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetExecution(exec_id string) *workflow_execution.WorkflowExecution {
 | 
					func GetExecution(exec_id string) *workflow_execution.WorkflowExecution {
 | 
				
			||||||
	res := oclib.NewRequest(oclib.LibDataEnum(oclib.WORKFLOW_EXECUTION), "", conf.GetConfig().PeerID, []string{}, nil).LoadOne(exec_id)
 | 
						res := oclib.NewRequest(oclib.LibDataEnum(oclib.WORKFLOW_EXECUTION), "", conf.GetConfig().PeerID, []string{}, nil).LoadOne(exec_id)
 | 
				
			||||||
	if res.Code != 200 {
 | 
						if res.Code != 200 {
 | 
				
			||||||
		logger := oclib.GetLogger()
 | 
							logger := oclib.GetLogger()
 | 
				
			||||||
		logger.Error().Msg("Could not retrieve workflow ID from execution ID " + exec_id)
 | 
							logger.Error().Msg("Error retrieving execution " + exec_id)
 | 
				
			||||||
 | 
							logger.Error().Msg(res.Err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return res.ToWorkflowExecution()
 | 
						return res.ToWorkflowExecution()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetLogger() zerolog.Logger {
 | 
				
			||||||
 | 
						onceLogger.Do(func(){
 | 
				
			||||||
 | 
							logger = logs.CreateLogger("oc-monitord")
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return logger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetWFLogger(workflowName string) zerolog.Logger {
 | 
				
			||||||
 | 
						onceWF.Do(func(){
 | 
				
			||||||
 | 
							wf_logger = logger.With().
 | 
				
			||||||
 | 
												Str("argo_name", workflowName).
 | 
				
			||||||
 | 
												Str("workflow_id", conf.GetConfig().
 | 
				
			||||||
 | 
												WorkflowID).Str("workflow_execution_id", conf.GetConfig().ExecutionID).Logger()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return wf_logger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,18 +4,26 @@ import (
 | 
				
			|||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"oc-monitord/utils"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	oclib "cloud.o-forge.io/core/oc-lib"
 | 
						oclib "cloud.o-forge.io/core/oc-lib"
 | 
				
			||||||
 | 
						"cloud.o-forge.io/core/oc-lib/logs"
 | 
				
			||||||
	"cloud.o-forge.io/core/oc-lib/models/peer"
 | 
						"cloud.o-forge.io/core/oc-lib/models/peer"
 | 
				
			||||||
	"cloud.o-forge.io/core/oc-lib/tools"
 | 
						tools "cloud.o-forge.io/core/oc-lib/tools"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type AdmiraltySetter struct {
 | 
					type AdmiraltySetter struct {
 | 
				
			||||||
	Id	string		// ID to identify the execution, correspond to workflow_executions id
 | 
						Id			string				// ID to identify the execution, correspond to workflow_executions id
 | 
				
			||||||
 | 
						NodeName 	string	// Allows to retrieve the name of the node used for this execution on each peer {"peerId": "nodeName"}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *AdmiraltySetter) InitializeAdmiralty(localPeerID string,remotePeerID string) error {
 | 
					func (s *AdmiraltySetter) InitializeAdmiralty(localPeerID string,remotePeerID string) error {
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
						logger := logs.GetLogger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER),"",localPeerID,nil,nil).LoadOne(remotePeerID)
 | 
						data := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER),"",localPeerID,nil,nil).LoadOne(remotePeerID)
 | 
				
			||||||
	if data.Code != 200 {
 | 
						if data.Code != 200 {
 | 
				
			||||||
		logger.Error().Msg("Error while trying to instantiate remote peer " + remotePeerID)
 | 
							logger.Error().Msg("Error while trying to instantiate remote peer " + remotePeerID)
 | 
				
			||||||
@@ -23,76 +31,116 @@ func (s *AdmiraltySetter) InitializeAdmiralty(localPeerID string,remotePeerID st
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	remotePeer := data.ToPeer()
 | 
						remotePeer := data.ToPeer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data = oclib.NewRequest(oclib.LibDataEnum(oclib.PEER),"",localPeerID,nil,nil).LoadOne(localPeerID)
 | 
								data = oclib.NewRequest(oclib.LibDataEnum(oclib.PEER),"",localPeerID,nil,nil).LoadOne(localPeerID)
 | 
				
			||||||
	if data.Code != 200 {
 | 
								if data.Code != 200 {
 | 
				
			||||||
		logger.Error().Msg("Error while trying to instantiate local peer " + remotePeerID)
 | 
									logger.Error().Msg("Error while trying to instantiate local peer " + remotePeerID)
 | 
				
			||||||
		return fmt.Errorf(data.Err)
 | 
									return fmt.Errorf(data.Err)
 | 
				
			||||||
	}
 | 
								}
 | 
				
			||||||
	localPeer := data.ToPeer()
 | 
								localPeer := data.ToPeer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	caller := tools.NewHTTPCaller(
 | 
								caller := tools.NewHTTPCaller(
 | 
				
			||||||
		map[tools.DataType]map[tools.METHOD]string{
 | 
									map[tools.DataType]map[tools.METHOD]string{
 | 
				
			||||||
			tools.ADMIRALTY_SOURCE: {
 | 
										tools.ADMIRALTY_SOURCE: {
 | 
				
			||||||
				tools.POST :"/:id",
 | 
											tools.POST :"/:id",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										tools.ADMIRALTY_KUBECONFIG: {
 | 
				
			||||||
 | 
											tools.GET:"/:id",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										tools.ADMIRALTY_SECRET: {
 | 
				
			||||||
 | 
											tools.POST:"/:id/" + remotePeerID,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										tools.ADMIRALTY_TARGET: {
 | 
				
			||||||
 | 
											tools.POST:"/:id/" + remotePeerID,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										tools.ADMIRALTY_NODES: {
 | 
				
			||||||
 | 
											tools.GET:"/:id/" + remotePeerID,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			tools.ADMIRALTY_KUBECONFIG: {
 | 
							)
 | 
				
			||||||
				tools.GET:"/:id",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			tools.ADMIRALTY_SECRET: {
 | 
					 | 
				
			||||||
				tools.POST:"/:id",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			tools.ADMIRALTY_TARGET: {
 | 
					 | 
				
			||||||
				tools.POST:"/:id",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			tools.ADMIRALTY_NODES: {
 | 
					 | 
				
			||||||
				tools.GET:"/:id",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	logger.Info().Msg(" Creating the Admiralty Source on " + remotePeerID + " ns-" + s.Id + "\n\n")
 | 
						logger.Info().Msg("\n\n  Creating the Admiralty Source on " + remotePeerID + " ns-" + s.Id)
 | 
				
			||||||
	_ = s.callRemoteExecution(remotePeer, http.StatusCreated,caller, s.Id, tools.ADMIRALTY_SOURCE, tools.POST, nil)
 | 
						s.callRemoteExecution(remotePeer, []int{http.StatusCreated, http.StatusConflict},caller, s.Id, tools.ADMIRALTY_SOURCE, tools.POST, nil, true)
 | 
				
			||||||
	logger.Info().Msg(" Retrieving kubeconfig with the secret on " + remotePeerID + " ns-" + s.Id + "\n\n")
 | 
						logger.Info().Msg("\n\n  Retrieving kubeconfig with the secret on " + remotePeerID + " ns-" + s.Id)
 | 
				
			||||||
	kubeconfig := s.getKubeconfig(remotePeer, caller)
 | 
						kubeconfig := s.getKubeconfig(remotePeer, caller)
 | 
				
			||||||
	logger.Info().Msg(" Creating a secret from the kubeconfig " + localPeerID + " ns-" + s.Id + "\n\n")
 | 
						logger.Info().Msg("\n\n  Creating a secret from the kubeconfig " + localPeerID + " ns-" + s.Id)
 | 
				
			||||||
	_ = s.callRemoteExecution(localPeer, http.StatusCreated, caller,s.Id, tools.ADMIRALTY_SECRET, tools.POST,kubeconfig)
 | 
						s.callRemoteExecution(localPeer, []int{http.StatusCreated}, caller,s.Id, tools.ADMIRALTY_SECRET, tools.POST,kubeconfig, true)
 | 
				
			||||||
	logger.Info().Msg(" Creating the Admiralty Target on " + localPeerID + " ns-" + s.Id + "\n\n")
 | 
						logger.Info().Msg("\n\n Creating the Admiralty Target on " + localPeerID + " in namespace " + s.Id )
 | 
				
			||||||
	_ = s.callRemoteExecution(localPeer,http.StatusCreated,caller,s.Id,tools.ADMIRALTY_TARGET,tools.POST, nil)
 | 
						s.callRemoteExecution(localPeer,[]int{http.StatusCreated, http.StatusConflict},caller,s.Id,tools.ADMIRALTY_TARGET,tools.POST, nil, true)
 | 
				
			||||||
	logger.Info().Msg(" Checking for the creation of the admiralty node on " + localPeerID + " ns-" + s.Id + "\n\n")
 | 
						logger.Info().Msg("\n\n  Checking for the creation of the admiralty node on " + localPeerID + " ns-" + s.Id)
 | 
				
			||||||
	_ = s.callRemoteExecution(localPeer,http.StatusOK,caller,s.Id,tools.ADMIRALTY_NODES,tools.GET, nil)
 | 
						s.checkNodeStatus(localPeer,caller)
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *AdmiraltySetter) getKubeconfig(peer *peer.Peer, caller *tools.HTTPCaller) map[string]string {
 | 
					func (s *AdmiraltySetter) getKubeconfig(peer *peer.Peer, caller *tools.HTTPCaller) map[string]string {
 | 
				
			||||||
	var kubedata map[string]string
 | 
						var kubedata map[string]string
 | 
				
			||||||
	_ = s.callRemoteExecution(peer, http.StatusOK, caller, s.Id, tools.ADMIRALTY_KUBECONFIG, tools.GET, nil)
 | 
						s.callRemoteExecution(peer, []int{http.StatusOK}, caller, s.Id, tools.ADMIRALTY_KUBECONFIG, tools.GET, nil, true)
 | 
				
			||||||
	if caller.LastResults["body"] == nil || len(caller.LastResults["body"].([]byte)) == 0 {
 | 
						if caller.LastResults["body"] == nil || len(caller.LastResults["body"].([]byte)) == 0 {
 | 
				
			||||||
		fmt.Println("Something went wrong when retrieving data from Get call for kubeconfig")
 | 
							l := utils.GetLogger()
 | 
				
			||||||
 | 
							l.Error().Msg("Something went wrong when retrieving data from Get call for kubeconfig")
 | 
				
			||||||
		panic(0)
 | 
							panic(0)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err := json.Unmarshal(caller.LastResults["body"].([]byte), &kubedata)
 | 
						err := json.Unmarshal(caller.LastResults["body"].([]byte), &kubedata)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println("Something went wrong when unmarshalling data from Get call for kubeconfig")
 | 
							l := utils.GetLogger()
 | 
				
			||||||
 | 
							l.Error().Msg("Something went wrong when unmarshalling data from Get call for kubeconfig")
 | 
				
			||||||
		panic(0)
 | 
							panic(0)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return kubedata
 | 
						return kubedata
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (*AdmiraltySetter) callRemoteExecution(peer *peer.Peer, expectedCode int,caller *tools.HTTPCaller, dataID string, dt tools.DataType, method tools.METHOD, body interface{}) *peer.PeerExecution {
 | 
					func (*AdmiraltySetter) callRemoteExecution(peer *peer.Peer, expectedCode []int,caller *tools.HTTPCaller, dataID string, dt tools.DataType, method tools.METHOD, body interface{}, panicCode bool) {
 | 
				
			||||||
	resp, err := peer.LaunchPeerExecution(peer.UUID, dataID, dt, method, body, caller)
 | 
						l := utils.GetLogger()
 | 
				
			||||||
 | 
						_, err := peer.LaunchPeerExecution(peer.UUID, dataID, dt, method, body, caller)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println("Error when executing on peer at", peer.Url)
 | 
							l.Error().Msg("Error when executing on peer at" + peer.Url)
 | 
				
			||||||
		fmt.Println(err)
 | 
							l.Error().Msg(err.Error())
 | 
				
			||||||
		panic(0)
 | 
							panic(0)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if caller.LastResults["code"].(int) != expectedCode {
 | 
						if !slices.Contains(expectedCode, caller.LastResults["code"].(int)) {
 | 
				
			||||||
		fmt.Println("Didn't receive the expected code :", caller.LastResults["code"], "when expecting", expectedCode)
 | 
							l.Error().Msg(fmt.Sprint("Didn't receive the expected code :", caller.LastResults["code"], "when expecting", expectedCode))
 | 
				
			||||||
		fmt.Println(string(caller.LastResults["body"].(byte)))
 | 
							if _, ok := caller.LastResults["body"]; ok {
 | 
				
			||||||
		panic(0)
 | 
								l.Info().Msg(string(caller.LastResults["body"].([]byte)))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if panicCode {
 | 
				
			||||||
 | 
								panic(0)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *AdmiraltySetter) storeNodeName(caller *tools.HTTPCaller){
 | 
				
			||||||
 | 
						var data map[string]interface{}
 | 
				
			||||||
 | 
						if resp, ok := caller.LastResults["body"]; ok {
 | 
				
			||||||
 | 
							json.Unmarshal(resp.([]byte), &data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if node, ok := data["node"]; ok {
 | 
				
			||||||
 | 
							metadata := node.(map[string]interface{})["metadata"]
 | 
				
			||||||
 | 
							name := metadata.(map[string]interface{})["name"].(string)
 | 
				
			||||||
 | 
							s.NodeName = name
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							l := utils.GetLogger()
 | 
				
			||||||
 | 
							l.Error().Msg("Could not retrieve data about the recently created node")
 | 
				
			||||||
 | 
							panic(0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *AdmiraltySetter) checkNodeStatus(localPeer *peer.Peer, caller *tools.HTTPCaller){
 | 
				
			||||||
 | 
						for i := range(5) {
 | 
				
			||||||
 | 
							time.Sleep(10 * time.Second) // let some time for kube to generate the node
 | 
				
			||||||
 | 
							s.callRemoteExecution(localPeer,[]int{http.StatusOK},caller,s.Id,tools.ADMIRALTY_NODES,tools.GET, nil, false)
 | 
				
			||||||
 | 
							if caller.LastResults["code"] == 200 {
 | 
				
			||||||
 | 
								s.storeNodeName(caller)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if i == 5 {
 | 
				
			||||||
 | 
								logger.Error().Msg("Node on " + localPeer.Name + " was never found, panicking !")
 | 
				
			||||||
 | 
								panic(0)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logger.Info().Msg("Could not verify that node is up. Retrying...")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	return resp
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -8,15 +8,18 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"oc-monitord/conf"
 | 
						"oc-monitord/conf"
 | 
				
			||||||
	. "oc-monitord/models"
 | 
						. "oc-monitord/models"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tools2 "oc-monitord/tools"
 | 
						tools2 "oc-monitord/tools"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	oclib "cloud.o-forge.io/core/oc-lib"
 | 
						oclib "cloud.o-forge.io/core/oc-lib"
 | 
				
			||||||
 | 
						"cloud.o-forge.io/core/oc-lib/logs"
 | 
				
			||||||
	"cloud.o-forge.io/core/oc-lib/models/common/enum"
 | 
						"cloud.o-forge.io/core/oc-lib/models/common/enum"
 | 
				
			||||||
	"cloud.o-forge.io/core/oc-lib/models/resources"
 | 
						"cloud.o-forge.io/core/oc-lib/models/resources"
 | 
				
			||||||
	w "cloud.o-forge.io/core/oc-lib/models/workflow"
 | 
						w "cloud.o-forge.io/core/oc-lib/models/workflow"
 | 
				
			||||||
 | 
						"cloud.o-forge.io/core/oc-lib/models/workflow/graph"
 | 
				
			||||||
	"github.com/nwtgck/go-fakelish"
 | 
						"github.com/nwtgck/go-fakelish"
 | 
				
			||||||
	"github.com/rs/zerolog"
 | 
						"github.com/rs/zerolog"
 | 
				
			||||||
	"gopkg.in/yaml.v3"
 | 
						"gopkg.in/yaml.v3"
 | 
				
			||||||
@@ -25,11 +28,11 @@ import (
 | 
				
			|||||||
var logger zerolog.Logger
 | 
					var logger zerolog.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ArgoBuilder struct {
 | 
					type ArgoBuilder struct {
 | 
				
			||||||
	OriginWorkflow 	*w.Workflow
 | 
						OriginWorkflow *w.Workflow
 | 
				
			||||||
	Workflow       	Workflow
 | 
						Workflow       Workflow
 | 
				
			||||||
	Services       	[]*Service
 | 
						Services       []*Service
 | 
				
			||||||
	Timeout        	int
 | 
						Timeout        int
 | 
				
			||||||
	RemotePeers		[]string
 | 
						RemotePeers    []string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Workflow struct {
 | 
					type Workflow struct {
 | 
				
			||||||
@@ -52,17 +55,19 @@ func (b *Workflow) getDag() *Dag {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Spec struct {
 | 
					type Spec struct {
 | 
				
			||||||
	Entrypoint string                `yaml:"entrypoint"`
 | 
						ServiceAccountName	string					`yaml:"serviceAccountName,omitempty"`
 | 
				
			||||||
	Arguments  []Parameter           `yaml:"arguments,omitempty"`
 | 
						Entrypoint 			string                	`yaml:"entrypoint"`
 | 
				
			||||||
	Volumes    []VolumeClaimTemplate `yaml:"volumeClaimTemplates,omitempty"`
 | 
						Arguments  			[]Parameter           	`yaml:"arguments,omitempty"`
 | 
				
			||||||
	Templates  []Template            `yaml:"templates"`
 | 
						Volumes    			[]VolumeClaimTemplate 	`yaml:"volumeClaimTemplates,omitempty"`
 | 
				
			||||||
	Timeout    int                   `yaml:"activeDeadlineSeconds,omitempty"`
 | 
						Templates  			[]Template            	`yaml:"templates"`
 | 
				
			||||||
 | 
						Timeout    			int                   	`yaml:"activeDeadlineSeconds,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: found on a processing instance linked to storage
 | 
					// TODO: found on a processing instance linked to storage
 | 
				
			||||||
// add s3, gcs, azure, etc if needed on a link between processing and storage
 | 
					// add s3, gcs, azure, etc if needed on a link between processing and storage
 | 
				
			||||||
func (b *ArgoBuilder) CreateDAG(namespace string, write bool) (string, int, []string, []string, error) {
 | 
					func (b *ArgoBuilder) CreateDAG(namespace string, write bool) ( int, []string, []string, error) {
 | 
				
			||||||
	fmt.Println("Creating DAG", b.OriginWorkflow.Graph.Items)
 | 
						logger = logs.GetLogger()
 | 
				
			||||||
 | 
						logger.Info().Msg(fmt.Sprint("Creating DAG ", b.OriginWorkflow.Graph.Items))
 | 
				
			||||||
	// handle services by checking if there is only one processing with hostname and port
 | 
						// handle services by checking if there is only one processing with hostname and port
 | 
				
			||||||
	firstItems, lastItems, volumes := b.createTemplates(namespace)
 | 
						firstItems, lastItems, volumes := b.createTemplates(namespace)
 | 
				
			||||||
	b.createVolumes(volumes)
 | 
						b.createVolumes(volumes)
 | 
				
			||||||
@@ -70,30 +75,16 @@ func (b *ArgoBuilder) CreateDAG(namespace string, write bool) (string, int, []st
 | 
				
			|||||||
	if b.Timeout > 0 {
 | 
						if b.Timeout > 0 {
 | 
				
			||||||
		b.Workflow.Spec.Timeout = b.Timeout
 | 
							b.Workflow.Spec.Timeout = b.Timeout
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						b.Workflow.Spec.ServiceAccountName = "sa-" + namespace
 | 
				
			||||||
	b.Workflow.Spec.Entrypoint = "dag"
 | 
						b.Workflow.Spec.Entrypoint = "dag"
 | 
				
			||||||
	b.Workflow.ApiVersion = "argoproj.io/v1alpha1"
 | 
						b.Workflow.ApiVersion = "argoproj.io/v1alpha1"
 | 
				
			||||||
	b.Workflow.Kind = "Workflow"
 | 
						b.Workflow.Kind = "Workflow"
 | 
				
			||||||
	if !write {
 | 
						if !write {
 | 
				
			||||||
		return "", len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
 | 
							return len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	random_name := fakelish.GenerateFakeWord(5, 8) + "-" + fakelish.GenerateFakeWord(5, 8)
 | 
						
 | 
				
			||||||
	b.Workflow.Metadata.Name = "oc-monitor-" + random_name
 | 
						
 | 
				
			||||||
	logger = oclib.GetLogger()
 | 
						return  len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
 | 
				
			||||||
	yamlified, err := yaml.Marshal(b.Workflow)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error().Msg("Could not transform object to yaml file")
 | 
					 | 
				
			||||||
		return "", 0, firstItems, lastItems, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Give a unique name to each argo file with its timestamp DD:MM:YYYY_hhmmss
 | 
					 | 
				
			||||||
	current_timestamp := time.Now().Format("02_01_2006_150405")
 | 
					 | 
				
			||||||
	file_name := random_name + "_" + current_timestamp + ".yml"
 | 
					 | 
				
			||||||
	workflows_dir := "./argo_workflows/"
 | 
					 | 
				
			||||||
	err = os.WriteFile(workflows_dir+file_name, []byte(yamlified), 0660)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error().Msg("Could not write the yaml file")
 | 
					 | 
				
			||||||
		return "", 0, firstItems, lastItems, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return workflows_dir + file_name, len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
func (b *ArgoBuilder) createTemplates(namespace string) ([]string, []string, []VolumeMount) {
 | 
					func (b *ArgoBuilder) createTemplates(namespace string) ([]string, []string, []VolumeMount) {
 | 
				
			||||||
@@ -101,10 +92,10 @@ func (b *ArgoBuilder) createTemplates(namespace string) ([]string, []string, []V
 | 
				
			|||||||
	firstItems := []string{}
 | 
						firstItems := []string{}
 | 
				
			||||||
	lastItems := []string{}
 | 
						lastItems := []string{}
 | 
				
			||||||
	items := b.OriginWorkflow.GetGraphItems(b.OriginWorkflow.Graph.IsProcessing)
 | 
						items := b.OriginWorkflow.GetGraphItems(b.OriginWorkflow.Graph.IsProcessing)
 | 
				
			||||||
	fmt.Println("Creating templates", len(items))
 | 
						logger.Info().Msg(fmt.Sprint("Creating templates", len(items)))
 | 
				
			||||||
	for _, item := range b.OriginWorkflow.GetGraphItems(b.OriginWorkflow.Graph.IsProcessing) {
 | 
						for _, item := range b.OriginWorkflow.GetGraphItems(b.OriginWorkflow.Graph.IsProcessing) {
 | 
				
			||||||
		instance := item.Processing.GetSelectedInstance()
 | 
							instance := item.Processing.GetSelectedInstance()
 | 
				
			||||||
		fmt.Println("Creating template for", item.Processing.GetName(), instance)
 | 
							logger.Info().Msg(fmt.Sprint("Creating template for", item.Processing.GetName(), instance))
 | 
				
			||||||
		if instance == nil || instance.(*resources.ProcessingInstance).Access == nil && instance.(*resources.ProcessingInstance).Access.Container != nil {
 | 
							if instance == nil || instance.(*resources.ProcessingInstance).Access == nil && instance.(*resources.ProcessingInstance).Access.Container != nil {
 | 
				
			||||||
			logger.Error().Msg("Not enough configuration setup, template can't be created : " + item.Processing.GetName())
 | 
								logger.Error().Msg("Not enough configuration setup, template can't be created : " + item.Processing.GetName())
 | 
				
			||||||
			return firstItems, lastItems, volumes
 | 
								return firstItems, lastItems, volumes
 | 
				
			||||||
@@ -122,7 +113,7 @@ func (b *ArgoBuilder) createTemplates(namespace string) ([]string, []string, []V
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		subBuilder := ArgoBuilder{OriginWorkflow: realWorkflow.(*w.Workflow), Timeout: b.Timeout}
 | 
							subBuilder := ArgoBuilder{OriginWorkflow: realWorkflow.(*w.Workflow), Timeout: b.Timeout}
 | 
				
			||||||
		_, _, fi, li, err := subBuilder.CreateDAG(namespace, false)
 | 
							_, fi, li, err := subBuilder.CreateDAG(namespace, false)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			logger.Error().Msg("Error creating the subworkflow : " + err.Error())
 | 
								logger.Error().Msg("Error creating the subworkflow : " + err.Error())
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
@@ -187,11 +178,14 @@ func (b *ArgoBuilder) createArgoTemplates(namespace string,
 | 
				
			|||||||
	lastItems []string) ([]VolumeMount, []string, []string) {
 | 
						lastItems []string) ([]VolumeMount, []string, []string) {
 | 
				
			||||||
	_, firstItems, lastItems = b.addTaskToArgo(b.Workflow.getDag(), id, processing, firstItems, lastItems)
 | 
						_, firstItems, lastItems = b.addTaskToArgo(b.Workflow.getDag(), id, processing, firstItems, lastItems)
 | 
				
			||||||
	template := &Template{Name: getArgoName(processing.GetName(), id)}
 | 
						template := &Template{Name: getArgoName(processing.GetName(), id)}
 | 
				
			||||||
	fmt.Println("Creating template for", template.Name)
 | 
						logger.Info().Msg(fmt.Sprint("Creating template for", template.Name))
 | 
				
			||||||
	isReparted, peerId := b.isProcessingReparted(*processing,id)
 | 
						isReparted, peerId := b.isProcessingReparted(*processing, id)
 | 
				
			||||||
	template.CreateContainer(processing, b.Workflow.getDag())
 | 
						template.CreateContainer(processing, b.Workflow.getDag())
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	if isReparted {
 | 
						if isReparted {
 | 
				
			||||||
 | 
							logger.Debug().Msg("Reparted processing, on " + peerId)
 | 
				
			||||||
		b.RemotePeers = append(b.RemotePeers, peerId)
 | 
							b.RemotePeers = append(b.RemotePeers, peerId)
 | 
				
			||||||
 | 
							template.AddAdmiraltyAnnotations(peerId)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// get datacenter from the processing
 | 
						// get datacenter from the processing
 | 
				
			||||||
	if processing.IsService {
 | 
						if processing.IsService {
 | 
				
			||||||
@@ -199,55 +193,33 @@ func (b *ArgoBuilder) createArgoTemplates(namespace string,
 | 
				
			|||||||
		template.Metadata.Labels = make(map[string]string)
 | 
							template.Metadata.Labels = make(map[string]string)
 | 
				
			||||||
		template.Metadata.Labels["app"] = "oc-service-" + processing.GetName() // Construct the template for the k8s service and add a link in graph between k8s service and processing
 | 
							template.Metadata.Labels["app"] = "oc-service-" + processing.GetName() // Construct the template for the k8s service and add a link in graph between k8s service and processing
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	related := b.OriginWorkflow.GetByRelatedProcessing(id, b.OriginWorkflow.Graph.IsStorage)
 | 
					
 | 
				
			||||||
 | 
						volumes = b.addStorageAnnotations(id, template, namespace, volumes)
 | 
				
			||||||
 | 
						b.Workflow.Spec.Templates = append(b.Workflow.Spec.Templates, *template)
 | 
				
			||||||
 | 
						return volumes, firstItems, lastItems
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *ArgoBuilder) addStorageAnnotations(id string, template *Template, namespace string, volumes []VolumeMount) []VolumeMount {
 | 
				
			||||||
 | 
						related := b.OriginWorkflow.GetByRelatedProcessing(id, b.OriginWorkflow.Graph.IsStorage) // Retrieve all of the storage node linked to the processing for which we create the template
 | 
				
			||||||
	for _, r := range related {
 | 
						for _, r := range related {
 | 
				
			||||||
		storage := r.Node.(*resources.StorageResource)
 | 
							storage := r.Node.(*resources.StorageResource)
 | 
				
			||||||
		for _, linkToStorage := range r.Links {
 | 
							for _, linkToStorage := range r.Links {
 | 
				
			||||||
			for _, rw := range linkToStorage.StorageLinkInfos {
 | 
								for _, rw := range linkToStorage.StorageLinkInfos {
 | 
				
			||||||
				art := Artifact{Path: template.ReplacePerEnv(rw.Source, linkToStorage.Env)}
 | 
									var art Artifact
 | 
				
			||||||
 | 
									artifactBaseName := strings.Join(strings.Split(storage.GetName(), " "), "-") + "-" + strings.Replace(rw.FileName, ".", "-", -1) //  Parameter/Artifact name must consist of alpha-numeric characters, '_' or '-'
 | 
				
			||||||
				if rw.Write {
 | 
									if rw.Write {
 | 
				
			||||||
					art.Name = storage.GetName() + "-" + rw.Destination + "-input-write"
 | 
										art = Artifact{Path: template.ReplacePerEnv(rw.Source, linkToStorage.Env)} // When we are writing to the s3 the Path element is the path to the file in the pod
 | 
				
			||||||
 | 
										art.Name = artifactBaseName + "-input-write"
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					art.Name = storage.GetName() + "-" + rw.Destination + "-input-read"
 | 
										art = Artifact{Path: template.ReplacePerEnv(rw.Destination+"/"+rw.FileName, linkToStorage.Env)} // When we are reading from the s3 the Path element in pod should be the destination of the file
 | 
				
			||||||
 | 
										art.Name = artifactBaseName + "-input-read"
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if storage.StorageType == enum.S3 {
 | 
									if storage.StorageType == enum.S3 {
 | 
				
			||||||
					art.S3 = &Key{
 | 
					
 | 
				
			||||||
						Key:      template.ReplacePerEnv(rw.Destination+"/"+rw.FileName, linkToStorage.Env),
 | 
										b.addS3annotations(&art, template, rw, linkToStorage, storage, namespace)
 | 
				
			||||||
						Insecure: true, // temporary
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					sel := storage.GetSelectedInstance()
 | 
					 | 
				
			||||||
					if sel != nil {
 | 
					 | 
				
			||||||
						if sel.(*resources.StorageResourceInstance).Credentials != nil {
 | 
					 | 
				
			||||||
							tool, err := tools2.NewService(conf.GetConfig().Mode)
 | 
					 | 
				
			||||||
							if err != nil || tool == nil {
 | 
					 | 
				
			||||||
								logger.Error().Msg("Could not create the access secret")
 | 
					 | 
				
			||||||
							} else {
 | 
					 | 
				
			||||||
								id, err := tool.CreateAccessSecret(namespace,
 | 
					 | 
				
			||||||
									sel.(*resources.StorageResourceInstance).Credentials.Login,
 | 
					 | 
				
			||||||
									sel.(*resources.StorageResourceInstance).Credentials.Pass)
 | 
					 | 
				
			||||||
								if err == nil {
 | 
					 | 
				
			||||||
									art.S3.AccessKeySecret = &Secret{
 | 
					 | 
				
			||||||
										Name: id,
 | 
					 | 
				
			||||||
										Key:  "access-key",
 | 
					 | 
				
			||||||
									}
 | 
					 | 
				
			||||||
									art.S3.SecretKeySecret = &Secret{
 | 
					 | 
				
			||||||
										Name: id,
 | 
					 | 
				
			||||||
										Key:  "secret-key",
 | 
					 | 
				
			||||||
									}
 | 
					 | 
				
			||||||
								}
 | 
					 | 
				
			||||||
							}
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						art.S3.Key = strings.ReplaceAll(art.S3.Key, sel.(*resources.StorageResourceInstance).Source+"/", "")
 | 
					 | 
				
			||||||
						art.S3.Key = strings.ReplaceAll(art.S3.Key, sel.(*resources.StorageResourceInstance).Source, "")
 | 
					 | 
				
			||||||
						splits := strings.Split(art.S3.EndPoint, "/")
 | 
					 | 
				
			||||||
						if len(splits) > 1 {
 | 
					 | 
				
			||||||
							art.S3.Bucket = splits[0]
 | 
					 | 
				
			||||||
							art.S3.EndPoint = strings.Join(splits[1:], "/")
 | 
					 | 
				
			||||||
						} else {
 | 
					 | 
				
			||||||
							art.S3.Bucket = splits[0]
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if rw.Write {
 | 
									if rw.Write {
 | 
				
			||||||
					template.Outputs.Artifacts = append(template.Inputs.Artifacts, art)
 | 
										template.Outputs.Artifacts = append(template.Inputs.Artifacts, art)
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
@@ -268,14 +240,99 @@ func (b *ArgoBuilder) createArgoTemplates(namespace string,
 | 
				
			|||||||
			}, volumes)
 | 
								}, volumes)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	b.Workflow.Spec.Templates = append(b.Workflow.Spec.Templates, *template)
 | 
						return volumes
 | 
				
			||||||
	return volumes, firstItems, lastItems
 | 
					}	
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *ArgoBuilder) addS3annotations(art *Artifact, template *Template, rw graph.StorageProcessingGraphLink, linkToStorage graph.GraphLink, storage *resources.StorageResource, namespace string) {
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
						art.S3 = &Key{
 | 
				
			||||||
 | 
							// Key:      template.ReplacePerEnv(rw.Destination+"/"+rw.FileName, linkToStorage.Env),
 | 
				
			||||||
 | 
							Insecure: true, // temporary
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if rw.Write {
 | 
				
			||||||
 | 
							art.S3.Key = rw.Destination + "/" + rw.FileName
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							art.S3.Key = rw.Source 
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sel := storage.GetSelectedInstance()
 | 
				
			||||||
 | 
						// v0.1 : add the storage.Source to the s3 object
 | 
				
			||||||
 | 
						// v0.2 : test if the storage.Source  exists in the configMap and quit if not
 | 
				
			||||||
 | 
						// v1 	: v0.2 + if doesn't exist edit/create the configMap with the response from API call
 | 
				
			||||||
 | 
						if sel != nil {
 | 
				
			||||||
 | 
							b.addAuthInformation(storage, namespace, art)
 | 
				
			||||||
 | 
							art.S3.Bucket = namespace 											// DEFAULT : will need to update this to create an unique
 | 
				
			||||||
 | 
							art.S3.EndPoint = sel.(*resources.StorageResourceInstance).Source 
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *ArgoBuilder) addAuthInformation(storage *resources.StorageResource, namespace string, art *Artifact) {
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						sel := storage.GetSelectedInstance()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						tool, err := tools2.NewService(conf.GetConfig().Mode)
 | 
				
			||||||
 | 
						if err != nil || tool == nil {
 | 
				
			||||||
 | 
							logger.Fatal().Msg("Could not create the access secret :" + err.Error())
 | 
				
			||||||
 | 
							} 
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							secretName, err := b.SetupS3Credentials(storage, namespace, tool) 			// this method return should be updated once we have decided how to retrieve credentials  
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								art.S3.AccessKeySecret = &Secret{
 | 
				
			||||||
 | 
									Name: secretName,
 | 
				
			||||||
 | 
									Key:  "access-key",
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								art.S3.SecretKeySecret = &Secret{
 | 
				
			||||||
 | 
									Name: secretName,
 | 
				
			||||||
 | 
									Key:  "secret-key",
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						art.S3.Key = strings.ReplaceAll(art.S3.Key, sel.(*resources.StorageResourceInstance).Source+"/", "")
 | 
				
			||||||
 | 
						art.S3.Key = strings.ReplaceAll(art.S3.Key, sel.(*resources.StorageResourceInstance).Source, "")
 | 
				
			||||||
 | 
						splits := strings.Split(art.S3.EndPoint, "/")
 | 
				
			||||||
 | 
						if len(splits) > 1 {
 | 
				
			||||||
 | 
							art.S3.Bucket = splits[0]
 | 
				
			||||||
 | 
							art.S3.EndPoint = strings.Join(splits[1:], "/")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								art.S3.Bucket = splits[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *ArgoBuilder) SetupS3Credentials(storage *resources.StorageResource, namespace string, tool tools2.Tool) (string, error) {
 | 
				
			||||||
 | 
						s := tool.GetS3Secret(storage.UUID, namespace)
 | 
				
			||||||
 | 
						// var s *v1.Secret
 | 
				
			||||||
 | 
						accessKey, secretKey := retrieveMinioCredential("peer",namespace)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if s == nil {
 | 
				
			||||||
 | 
							id, err := tool.CreateAccessSecret(
 | 
				
			||||||
 | 
								accessKey,
 | 
				
			||||||
 | 
								secretKey,
 | 
				
			||||||
 | 
								storage.UUID,
 | 
				
			||||||
 | 
								namespace,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								l := oclib.GetLogger()
 | 
				
			||||||
 | 
								l.Fatal().Msg("Error when creating the secret holding credentials for S3 access in " + namespace + " : " + err.Error())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return id, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// s.Name = "toto"
 | 
				
			||||||
 | 
						return s.Name, nil
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This method needs to evolve to an API call to the peer passed as a parameter
 | 
				
			||||||
 | 
					func retrieveMinioCredential(peer string, namespace string) (string,string) {
 | 
				
			||||||
 | 
						return "hF9wRGog75JuMdshWeEZ", "OwXXJkVQyb5l1aVPdOegKOtDJGoP1dJYeo8O7mDW"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *ArgoBuilder) addTaskToArgo(dag *Dag, graphItemID string, processing *resources.ProcessingResource,
 | 
					func (b *ArgoBuilder) addTaskToArgo(dag *Dag, graphItemID string, processing *resources.ProcessingResource,
 | 
				
			||||||
	firstItems []string, lastItems []string) (*Dag, []string, []string) {
 | 
						firstItems []string, lastItems []string) (*Dag, []string, []string) {
 | 
				
			||||||
	unique_name := getArgoName(processing.GetName(), graphItemID)
 | 
							unique_name := getArgoName(processing.GetName(), graphItemID)
 | 
				
			||||||
	step := Task{Name: unique_name, Template: unique_name}
 | 
							step := Task{Name: unique_name, Template: unique_name}
 | 
				
			||||||
	instance := processing.GetSelectedInstance()
 | 
						instance := processing.GetSelectedInstance()
 | 
				
			||||||
	if instance != nil {
 | 
						if instance != nil {
 | 
				
			||||||
		for _, value := range instance.(*resources.ProcessingInstance).Env {
 | 
							for _, value := range instance.(*resources.ProcessingInstance).Env {
 | 
				
			||||||
@@ -335,7 +392,7 @@ func (b *ArgoBuilder) isArgoDependancy(id string) (bool, []string) {
 | 
				
			|||||||
	isDeps := false
 | 
						isDeps := false
 | 
				
			||||||
	for _, link := range b.OriginWorkflow.Graph.Links {
 | 
						for _, link := range b.OriginWorkflow.Graph.Links {
 | 
				
			||||||
		if _, ok := b.OriginWorkflow.Graph.Items[link.Destination.ID]; !ok {
 | 
							if _, ok := b.OriginWorkflow.Graph.Items[link.Destination.ID]; !ok {
 | 
				
			||||||
			fmt.Println("Could not find the source of the link", link.Destination.ID)
 | 
								logger.Info().Msg(fmt.Sprint("Could not find the source of the link", link.Destination.ID))
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		source := b.OriginWorkflow.Graph.Items[link.Destination.ID].Processing
 | 
							source := b.OriginWorkflow.Graph.Items[link.Destination.ID].Processing
 | 
				
			||||||
@@ -355,7 +412,7 @@ func (b *ArgoBuilder) isArgoDependancy(id string) (bool, []string) {
 | 
				
			|||||||
func (b *ArgoBuilder) getArgoDependencies(id string) (dependencies []string) {
 | 
					func (b *ArgoBuilder) getArgoDependencies(id string) (dependencies []string) {
 | 
				
			||||||
	for _, link := range b.OriginWorkflow.Graph.Links {
 | 
						for _, link := range b.OriginWorkflow.Graph.Links {
 | 
				
			||||||
		if _, ok := b.OriginWorkflow.Graph.Items[link.Source.ID]; !ok {
 | 
							if _, ok := b.OriginWorkflow.Graph.Items[link.Source.ID]; !ok {
 | 
				
			||||||
			fmt.Println("Could not find the source of the link", link.Source.ID)
 | 
								logger.Info().Msg(fmt.Sprint("Could not find the source of the link", link.Source.ID))
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		source := b.OriginWorkflow.Graph.Items[link.Source.ID].Processing
 | 
							source := b.OriginWorkflow.Graph.Items[link.Source.ID].Processing
 | 
				
			||||||
@@ -377,16 +434,15 @@ func getArgoName(raw_name string, component_id string) (formatedName string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Verify if a processing resource is attached to another Compute than the one hosting
 | 
					// Verify if a processing resource is attached to another Compute than the one hosting
 | 
				
			||||||
// the current Open Cloud instance. If true return the peer ID to contact
 | 
					// the current Open Cloud instance. If true return the peer ID to contact
 | 
				
			||||||
func (b *ArgoBuilder) isProcessingReparted(processing resources.ProcessingResource, graphID string) (bool,string) {
 | 
					func (b *ArgoBuilder) isProcessingReparted(processing resources.ProcessingResource, graphID string) (bool, string) {
 | 
				
			||||||
	computeAttached := b.retrieveProcessingCompute(graphID)
 | 
						computeAttached := b.retrieveProcessingCompute(graphID)
 | 
				
			||||||
	if computeAttached == nil {
 | 
						if computeAttached == nil {
 | 
				
			||||||
		logger.Error().Msg("No compute was found attached to processing " + processing.Name + " : " + processing.UUID )
 | 
							logger.Error().Msg("No compute was found attached to processing " + processing.Name + " : " + processing.UUID)
 | 
				
			||||||
		panic(0)
 | 
							panic(0)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	// Creates an accessor srtictly for Peer Collection
 | 
						// Creates an accessor srtictly for Peer Collection
 | 
				
			||||||
	req := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER),"","",nil,nil)
 | 
						req := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER), "", "", nil, nil)
 | 
				
			||||||
	if req == nil {
 | 
						if req == nil {
 | 
				
			||||||
		fmt.Println("TODO : handle error when trying to create a request on the Peer Collection")
 | 
							fmt.Println("TODO : handle error when trying to create a request on the Peer Collection")
 | 
				
			||||||
		return false, ""
 | 
							return false, ""
 | 
				
			||||||
@@ -401,8 +457,8 @@ func (b *ArgoBuilder) isProcessingReparted(processing resources.ProcessingResour
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	peer := *res.ToPeer()
 | 
						peer := *res.ToPeer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	isNotReparted, _ := peer.IsMySelf()
 | 
						isNotReparted := peer.State == 1
 | 
				
			||||||
	fmt.Println("Result IsMySelf for ", peer.UUID ," : ", isNotReparted)
 | 
						logger.Info().Msg(fmt.Sprint("Result IsMySelf for ", peer.UUID, " : ", isNotReparted))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return !isNotReparted, peer.UUID
 | 
						return !isNotReparted, peer.UUID
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -411,12 +467,12 @@ func (b *ArgoBuilder) retrieveProcessingCompute(graphID string) *resources.Compu
 | 
				
			|||||||
	for _, link := range b.OriginWorkflow.Graph.Links {
 | 
						for _, link := range b.OriginWorkflow.Graph.Links {
 | 
				
			||||||
		// If a link contains the id of the processing
 | 
							// If a link contains the id of the processing
 | 
				
			||||||
		var oppositeId string
 | 
							var oppositeId string
 | 
				
			||||||
		if link.Source.ID == graphID{
 | 
							if link.Source.ID == graphID {
 | 
				
			||||||
			oppositeId = link.Destination.ID
 | 
								oppositeId = link.Destination.ID
 | 
				
			||||||
		} else if(link.Destination.ID == graphID){
 | 
							} else if link.Destination.ID == graphID {
 | 
				
			||||||
			oppositeId = link.Source.ID
 | 
								oppositeId = link.Source.ID
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		fmt.Println("OppositeId : ", oppositeId)
 | 
							
 | 
				
			||||||
		if oppositeId != "" {
 | 
							if oppositeId != "" {
 | 
				
			||||||
			dt, res := b.OriginWorkflow.Graph.GetResource(oppositeId)
 | 
								dt, res := b.OriginWorkflow.Graph.GetResource(oppositeId)
 | 
				
			||||||
			if dt == oclib.COMPUTE_RESOURCE {
 | 
								if dt == oclib.COMPUTE_RESOURCE {
 | 
				
			||||||
@@ -431,14 +487,44 @@ func (b *ArgoBuilder) retrieveProcessingCompute(graphID string) *resources.Compu
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Execute the last actions once the YAML file for the Argo Workflow is created
 | 
					// Execute the last actions once the YAML file for the Argo Workflow is created
 | 
				
			||||||
func (b *ArgoBuilder) CompleteBuild(executionsId string) error {
 | 
					func (b *ArgoBuilder) CompleteBuild(executionsId string) (string, error) {
 | 
				
			||||||
	fmt.Println("DEV :: Completing build")
 | 
						logger.Info().Msg(fmt.Sprint("DEV :: Completing build"))
 | 
				
			||||||
 | 
						setter := AdmiraltySetter{Id: executionsId}
 | 
				
			||||||
 | 
						// Setup admiralty for each node
 | 
				
			||||||
	for _, peer := range b.RemotePeers {
 | 
						for _, peer := range b.RemotePeers {
 | 
				
			||||||
		fmt.Println("DEV :: Launching Admiralty Setup for ", peer)
 | 
							logger.Info().Msg(fmt.Sprint("DEV :: Launching Admiralty Setup for ", peer))
 | 
				
			||||||
		setter := AdmiraltySetter{Id: executionsId}
 | 
					 | 
				
			||||||
		setter.InitializeAdmiralty(conf.GetConfig().PeerID,peer)
 | 
							setter.InitializeAdmiralty(conf.GetConfig().PeerID,peer)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
					
 | 
				
			||||||
 | 
						// Update the name of the admiralty node to use 
 | 
				
			||||||
 | 
						for _, template := range b.Workflow.Spec.Templates {
 | 
				
			||||||
 | 
							if len(template.Metadata.Annotations) > 0 {
 | 
				
			||||||
 | 
								if peerId, ok := template.Metadata.Annotations["multicluster.admiralty.io/clustername"]; ok {
 | 
				
			||||||
 | 
									template.Metadata.Annotations["multicluster.admiralty.io/clustername"] = "target-" + oclib.GetConcatenatedName(peerId, executionsId)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Generate the YAML file
 | 
				
			||||||
 | 
						random_name := fakelish.GenerateFakeWord(5, 8) + "-" + fakelish.GenerateFakeWord(5, 8)
 | 
				
			||||||
 | 
						b.Workflow.Metadata.Name = "oc-monitor-" + random_name
 | 
				
			||||||
 | 
						logger = oclib.GetLogger()
 | 
				
			||||||
 | 
						yamlified, err := yaml.Marshal(b.Workflow)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Error().Msg("Could not transform object to yaml file")
 | 
				
			||||||
 | 
							return "", err 
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Give a unique name to each argo file with its timestamp DD:MM:YYYY_hhmmss
 | 
				
			||||||
 | 
						current_timestamp := time.Now().Format("02_01_2006_150405")
 | 
				
			||||||
 | 
						file_name := random_name + "_" + current_timestamp + ".yml"
 | 
				
			||||||
 | 
						workflows_dir := "./argo_workflows/"
 | 
				
			||||||
 | 
						err = os.WriteFile(workflows_dir+file_name, []byte(yamlified), 0660)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Error().Msg("Could not write the yaml file")
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return workflows_dir + file_name, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -14,7 +14,7 @@ type WorflowDB struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Create the obj!ects from the mxgraphxml stored in the workflow given as a parameter
 | 
					// Create the obj!ects from the mxgraphxml stored in the workflow given as a parameter
 | 
				
			||||||
func (w *WorflowDB) LoadFrom(workflow_id string, peerID string) error {
 | 
					func (w *WorflowDB) LoadFrom(workflow_id string, peerID string) error {
 | 
				
			||||||
	fmt.Println("Loading workflow from " + workflow_id)
 | 
						logger.Info().Msg("Loading workflow from " + workflow_id)
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if w.Workflow, err = w.getWorkflow(workflow_id, peerID); err != nil {
 | 
						if w.Workflow, err = w.getWorkflow(workflow_id, peerID); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -27,7 +27,7 @@ func (w *WorflowDB) getWorkflow(workflow_id string, peerID string) (workflow *wo
 | 
				
			|||||||
	logger := oclib.GetLogger()
 | 
						logger := oclib.GetLogger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lib_data := oclib.NewRequest(oclib.LibDataEnum(oclib.WORKFLOW), "", peerID, []string{}, nil).LoadOne(workflow_id)
 | 
						lib_data := oclib.NewRequest(oclib.LibDataEnum(oclib.WORKFLOW), "", peerID, []string{}, nil).LoadOne(workflow_id)
 | 
				
			||||||
	fmt.Println("ERR", lib_data.Code, lib_data.Err)
 | 
						logger.Info().Msg(fmt.Sprint("ERR", lib_data.Code, lib_data.Err))
 | 
				
			||||||
	if lib_data.Code != 200 {
 | 
						if lib_data.Code != 200 {
 | 
				
			||||||
		logger.Error().Msg("Error loading the graph")
 | 
							logger.Error().Msg("Error loading the graph")
 | 
				
			||||||
		return workflow, errors.New(lib_data.Err)
 | 
							return workflow, errors.New(lib_data.Err)
 | 
				
			||||||
@@ -41,20 +41,20 @@ func (w *WorflowDB) getWorkflow(workflow_id string, peerID string) (workflow *wo
 | 
				
			|||||||
	return new_wf, nil
 | 
						return new_wf, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (w *WorflowDB) ExportToArgo(namespace string, timeout int) (*ArgoBuilder,string, int, error) {
 | 
					func (w *WorflowDB) ExportToArgo(namespace string, timeout int) (*ArgoBuilder, int, error) {
 | 
				
			||||||
	logger := oclib.GetLogger()
 | 
						logger := oclib.GetLogger()
 | 
				
			||||||
	fmt.Println("Exporting to Argo", w.Workflow)
 | 
						logger.Info().Msg(fmt.Sprint("Exporting to Argo", w.Workflow))
 | 
				
			||||||
	if len(w.Workflow.Name) == 0 || w.Workflow.Graph == nil {
 | 
						if len(w.Workflow.Name) == 0 || w.Workflow.Graph == nil {
 | 
				
			||||||
		return nil, "", 0, fmt.Errorf("can't export a graph that has not been loaded yet")
 | 
							return nil, 0, fmt.Errorf("can't export a graph that has not been loaded yet")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	argoBuilder := ArgoBuilder{OriginWorkflow: w.Workflow, Timeout: timeout}
 | 
						argoBuilder := ArgoBuilder{OriginWorkflow: w.Workflow, Timeout: timeout}
 | 
				
			||||||
	filename, stepMax, _, _, err := argoBuilder.CreateDAG(namespace, true)
 | 
						stepMax, _, _, err := argoBuilder.CreateDAG(namespace, true)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logger.Error().Msg("Could not create the argo file for " + w.Workflow.Name)
 | 
							logger.Error().Msg("Could not create the argo file for " + w.Workflow.Name)
 | 
				
			||||||
		return nil, "", 0, err
 | 
							return nil, 0, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &argoBuilder, filename, stepMax, nil
 | 
						return &argoBuilder, stepMax, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO implement this function
 | 
					// TODO implement this function
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user