Compare commits
35 Commits
80e81820a4
...
feature/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2d1746396 | ||
|
|
5f70feab59 | ||
|
|
fb8d994be3 | ||
|
|
6f7acee2df | ||
|
|
31580f1905 | ||
|
|
04d6001fec | ||
|
|
e2ceb6e58d | ||
|
|
9aefa18ea8 | ||
|
|
27fd603e36 | ||
|
|
c31184e2ec | ||
|
|
5d8143c93e | ||
|
|
77a9b0770e | ||
|
|
9a17623cab | ||
|
|
4963284056 | ||
|
|
df09585cc9 | ||
|
|
aa20edaf25 | ||
| 42ee6abcb6 | |||
| 08ade1af66 | |||
| 73e1747c91 | |||
| 32ce70da6e | |||
| def56e5822 | |||
| 37c561c5fe | |||
| 3f533a1bfb | |||
| 3fa2cd3336 | |||
| da9a7d3a49 | |||
| 788a3174ea | |||
| 47363566b2 | |||
| 697d7a7145 | |||
| e4874697bc | |||
| 99ae1e6629 | |||
| 91a87fbc4d | |||
| 34547e8b2f | |||
| df6e3d5a46 | |||
| a0b1117075 | |||
| a1d28f2563 |
24
Dockerfile
24
Dockerfile
@@ -1,12 +1,28 @@
|
||||
FROM golang:alpine AS builder
|
||||
FROM golang:alpine AS deps
|
||||
|
||||
WORKDIR /app
|
||||
COPY go.mod go.sum ./
|
||||
RUN sed -i '/replace/d' go.mod
|
||||
RUN go mod download -x
|
||||
|
||||
#----------------------------------------------------------------------------------------------
|
||||
|
||||
FROM golang:alpine AS builder
|
||||
LABEL maintainer="IRT PFN"
|
||||
ENV DOCKER_ENVIRONMENT=true
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=deps /go/pkg /go/pkg
|
||||
COPY --from=deps /app/go.mod /app/go.sum ./
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN go build .
|
||||
|
||||
FROM argoproj/argocd:latest
|
||||
FROM scratch
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY conf/docker_ocmonitord_conf.json /app/conf/
|
||||
COPY --from=builder /app/oc-monitord .
|
||||
COPY --from=builder /app/oc-monitord .
|
||||
|
||||
ENTRYPOINT ["./oc-monitord"]
|
||||
12
README.md
12
README.md
@@ -48,11 +48,17 @@ In rules add a new entry :
|
||||
This command **must return "yes"**
|
||||
|
||||
|
||||
# Notes features/admiralty-docker
|
||||
|
||||
- When executing monitord as a container we need to change any url with "localhost" to the container's host IP.
|
||||
|
||||
We can :
|
||||
- declare a new parameter 'HOST_IP'
|
||||
- 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.
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Logs the output of each pods :
|
||||
- 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`
|
||||
|
||||
|
||||
16
conf/conf.go
16
conf/conf.go
@@ -3,14 +3,22 @@ package conf
|
||||
import "sync"
|
||||
|
||||
type Config struct {
|
||||
MongoURL string
|
||||
Database string
|
||||
MongoURL string
|
||||
Database string
|
||||
LokiURL string
|
||||
NatsURL string
|
||||
NatsURL string
|
||||
ExecutionID string
|
||||
PeerID string
|
||||
Timeout int
|
||||
WorkflowID string
|
||||
Logs string
|
||||
Logs string
|
||||
Mode string
|
||||
KubeHost string
|
||||
KubePort string
|
||||
KubeCA string
|
||||
KubeCert string
|
||||
KubeData string
|
||||
ArgoHost string // when executed in a container will replace addresses with "localhost" in their url
|
||||
}
|
||||
|
||||
var instance *Config
|
||||
|
||||
5
docker_schedulerd.json
Normal file
5
docker_schedulerd.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"MONGO_URL":"mongodb://mongo:27017/",
|
||||
"NATS_URL":"nats://nats:4222",
|
||||
"MONGO_DATABASE":"DC_myDC"
|
||||
}
|
||||
101
go.mod
101
go.mod
@@ -1,56 +1,101 @@
|
||||
module oc-monitord
|
||||
|
||||
go 1.22.0
|
||||
go 1.23.1
|
||||
|
||||
toolchain go1.22.4
|
||||
toolchain go1.23.3
|
||||
|
||||
require (
|
||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250110163958-fd1c579ec418
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250313155727-88c88cac5bc9
|
||||
github.com/akamensky/argparse v1.4.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/goraz/onion 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
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beego/beego/v2 v2.3.1 // indirect
|
||||
github.com/beego/beego/v2 v2.3.7 // indirect
|
||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // 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/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/grpc v1.63.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/argoproj/argo-workflows/v3 v3.6.4
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
||||
github.com/biter777/countries v1.7.5 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // 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/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/klauspost/compress v1.17.10 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/nats-io/nats.go v1.37.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nats-io/nats.go v1.41.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.10 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/prometheus/client_golang v1.19.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.63.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.0 // indirect
|
||||
github.com/robfig/cron v1.2.0 // indirect
|
||||
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // 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/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.1 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.3 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/term v0.31.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/api v0.32.1
|
||||
k8s.io/apimachinery v0.32.1
|
||||
k8s.io/client-go v0.32.1
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
292
go.sum
292
go.sum
@@ -1,17 +1,27 @@
|
||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250110163958-fd1c579ec418 h1:24nc5qA6AkV6pIbJJeRASeUZmWuGkl+FO/r2oXazJ8w=
|
||||
cloud.o-forge.io/core/oc-lib v0.0.0-20250110163958-fd1c579ec418/go.mod h1:ya7Q+zHhaKM+XF6sAJ+avqHEVzaMnFJQih2X3TlTlGo=
|
||||
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.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/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/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
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/go.mod h1:2f5zB8CkbNCCO1od+kd1dWkVokqcuyvu+tc+Jwx1MZg=
|
||||
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.1/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4=
|
||||
github.com/beego/beego/v2 v2.3.7 h1:z4btKtjU/rfp5BiYHkGD2QPjK9i1E9GH+I7vfhn6Agk=
|
||||
github.com/beego/beego/v2 v2.3.7/go.mod h1:5cqHsOHJIxkq44tBpRvtDe59GuVRVv/9/tyVDxd5ce4=
|
||||
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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
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/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.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
@@ -20,54 +30,106 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/etcd-io/etcd v3.3.17+incompatible/go.mod h1:cdZ77EstHBwVtD6iTgzgvogwcjo9m4iOqoijouPJ4bs=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
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/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
|
||||
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
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/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.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/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
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/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
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/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
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.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
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.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
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/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
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/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||
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/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
|
||||
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
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/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
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/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.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
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.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
@@ -77,43 +139,59 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
|
||||
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
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/nats-io/nats.go v1.41.0 h1:PzxEva7fflkd+n87OtQTXqCTyLfIIMFJBpyccHLE2Ko=
|
||||
github.com/nats-io/nats.go v1.41.0/go.mod h1:wV73x0FSI/orHPSYoyMeJB+KajMDoWyXmFaRrrYaaTo=
|
||||
github.com/nats-io/nkeys v0.4.10 h1:glmRrpCmYLHByYcePvnTBEAwawwapjCPMjy2huw20wc=
|
||||
github.com/nats-io/nkeys v0.4.10/go.mod h1:OjRrnIKnWBFl+s4YK5ChQfvHP2fxqZexrKJoVVyWB3U=
|
||||
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/nwtgck/go-fakelish v0.1.3 h1:bA8/xa9hQmzppexIhBvdmztcd/PJ4SPuAUTBdMKZ8G4=
|
||||
github.com/nwtgck/go-fakelish v0.1.3/go.mod h1:2HC44/OwVWwOa/g3+P2jUM3FEHQ0ya4gyCSU19PPd3Y=
|
||||
github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
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/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
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.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
|
||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
||||
github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM=
|
||||
github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg=
|
||||
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
|
||||
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/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/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
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/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik=
|
||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
||||
github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 h1:v9ezJDHA1XGxViAUSIoO/Id7Fl63u6d0YmsAm+/p2hs=
|
||||
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/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skarademir/naturalsort v0.0.0-20150715044055-69a5d87bef62/go.mod h1:oIdVclZaltY1Nf7OQUkg1/2jImBJ+ZfKZuDIRSwk3p0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
@@ -125,16 +203,21 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
@@ -144,60 +227,149 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
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.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.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
|
||||
go.mongodb.org/mongo-driver v1.17.3/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-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-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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
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.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
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/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
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 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
|
||||
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
|
||||
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
|
||||
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
|
||||
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
219
logger/argo_logs.go
Normal file
219
logger/argo_logs.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"oc-monitord/tools"
|
||||
"oc-monitord/utils"
|
||||
"slices"
|
||||
"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, executionID string, watcher watch.Interface) {
|
||||
var argoWatcher *ArgoWatch
|
||||
var pods []string
|
||||
var node wfv1.NodeStatus
|
||||
|
||||
wfl := utils.GetWFLogger("")
|
||||
|
||||
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.Debug().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: executionID,
|
||||
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
|
||||
go logKubernetesPods(executionID, wfName, pod.Name, pl)
|
||||
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")
|
||||
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){
|
||||
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(wfName,podName,log)
|
||||
jsonified, _ := json.Marshal(podLog)
|
||||
logger.Info().Msg(string(jsonified))
|
||||
}
|
||||
}
|
||||
144
logger/local_argo_logs.go
Normal file
144
logger/local_argo_logs.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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")
|
||||
fmt.Println("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
|
||||
fmt.Println("new line")
|
||||
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"
|
||||
}
|
||||
|
||||
329
main.go
329
main.go
@@ -2,25 +2,31 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"oc-monitord/conf"
|
||||
"oc-monitord/models"
|
||||
l "oc-monitord/logger"
|
||||
u "oc-monitord/utils"
|
||||
"oc-monitord/workflow_builder"
|
||||
|
||||
oclib "cloud.o-forge.io/core/oc-lib"
|
||||
|
||||
"cloud.o-forge.io/core/oc-lib/logs"
|
||||
"cloud.o-forge.io/core/oc-lib/models/booking"
|
||||
"cloud.o-forge.io/core/oc-lib/models/peer"
|
||||
"cloud.o-forge.io/core/oc-lib/models/utils"
|
||||
"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
|
||||
"cloud.o-forge.io/core/oc-lib/tools"
|
||||
|
||||
tools2 "oc-monitord/tools"
|
||||
|
||||
"github.com/akamensky/argparse"
|
||||
"github.com/google/uuid"
|
||||
@@ -38,7 +44,6 @@ import (
|
||||
var logger zerolog.Logger
|
||||
var wf_logger zerolog.Logger
|
||||
var parser argparse.Parser
|
||||
var monitorLocal bool
|
||||
var workflowName string
|
||||
|
||||
const defaultConfigFile = "/etc/oc/ocmonitord_conf.json"
|
||||
@@ -47,20 +52,8 @@ const localConfigFile = "./conf/local_ocmonitord_conf.json"
|
||||
func main() {
|
||||
|
||||
os.Setenv("test_service", "true") // Only for service demo, delete before merging on main
|
||||
|
||||
monitorLocal = false
|
||||
// Test if monitor is launched outside (with parameters) or in a k8s environment (env variables sets)
|
||||
if os.Getenv("KUBERNETES_SERVICE_HOST") == "" {
|
||||
// Not in a k8s environment, get conf from parameters
|
||||
fmt.Println("Executes outside of k8s")
|
||||
parser = *argparse.NewParser("oc-monitord", "Launch the execution of a workflow given as a parameter and sends the produced logs to a loki database")
|
||||
loadConfig(false, &parser)
|
||||
} else {
|
||||
// Executed in a k8s environment
|
||||
fmt.Println("Executes inside a k8s")
|
||||
monitorLocal = true
|
||||
loadConfig(true, nil)
|
||||
}
|
||||
parser = *argparse.NewParser("oc-monitord", "Launch the execution of a workflow given as a parameter and sends the produced logs to a loki database")
|
||||
loadConfig(false, &parser)
|
||||
oclib.InitDaemon("oc-monitord")
|
||||
|
||||
oclib.SetConfig(
|
||||
@@ -71,15 +64,14 @@ func main() {
|
||||
conf.GetConfig().Logs,
|
||||
)
|
||||
|
||||
logger = logs.CreateLogger("oc-monitord")
|
||||
logger = u.GetLogger()
|
||||
|
||||
logger.Debug().Msg("Loki URL : " + conf.GetConfig().LokiURL)
|
||||
logger.Debug().Msg("Workflow executed : " + conf.GetConfig().ExecutionID)
|
||||
exec := u.GetExecution(conf.GetConfig().ExecutionID)
|
||||
conf.GetConfig().WorkflowID = exec.WorkflowID
|
||||
|
||||
wf_id := getWorkflowId(conf.GetConfig().ExecutionID)
|
||||
conf.GetConfig().WorkflowID = wf_id
|
||||
|
||||
logger.Debug().Msg("Starting construction of yaml argo for workflow :" + wf_id)
|
||||
logger.Debug().Msg("Starting construction of yaml argo for workflow :" + exec.WorkflowID)
|
||||
|
||||
if _, err := os.Stat("./argo_workflows/"); os.IsNotExist(err) {
|
||||
os.Mkdir("./argo_workflows/", 0755)
|
||||
@@ -89,156 +81,185 @@ func main() {
|
||||
// // create argo
|
||||
new_wf := workflow_builder.WorflowDB{}
|
||||
|
||||
err := new_wf.LoadFrom(conf.GetConfig().WorkflowID)
|
||||
err := new_wf.LoadFrom(conf.GetConfig().WorkflowID, conf.GetConfig().PeerID)
|
||||
if err != nil {
|
||||
|
||||
logger.Error().Msg("Could not retrieve workflow " + conf.GetConfig().WorkflowID + " from oc-catalog API")
|
||||
}
|
||||
|
||||
argo_file_path, stepMax, err := new_wf.ExportToArgo(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 {
|
||||
logger.Error().Msg("Could not create the Argo file for " + conf.GetConfig().WorkflowID)
|
||||
logger.Error().Msg(err.Error())
|
||||
}
|
||||
logger.Debug().Msg("Created :" + argo_file_path)
|
||||
|
||||
workflowName = getContainerName(argo_file_path)
|
||||
argoFilePath, err := builder.CompleteBuild(exec.ExecutionsID)
|
||||
if err != nil {
|
||||
logger.Error().Msg(err.Error())
|
||||
}
|
||||
|
||||
wf_logger = logger.With().Str("argo_name", workflowName).Str("workflow_id", conf.GetConfig().WorkflowID).Str("workflow_execution_id", conf.GetConfig().ExecutionID).Logger()
|
||||
workflowName = getContainerName(argoFilePath)
|
||||
|
||||
wf_logger := u.GetWFLogger(workflowName)
|
||||
wf_logger.Debug().Msg("Testing argo name")
|
||||
|
||||
executeWorkflow(argo_file_path, stepMax)
|
||||
|
||||
}
|
||||
|
||||
// Return the Workflow ID associated to a workflow execution object
|
||||
func getWorkflowId(exec_id string) string {
|
||||
res := oclib.LoadOne(oclib.LibDataEnum(oclib.WORKFLOW_EXECUTION), exec_id)
|
||||
if res.Code != 200 {
|
||||
logger.Error().Msg("Could not retrieve workflow ID from execution ID " + exec_id)
|
||||
return ""
|
||||
if conf.GetConfig().KubeHost == "" {
|
||||
// Not in a k8s environment, get conf from parameters
|
||||
fmt.Println("Executes outside of k8s")
|
||||
executeOutside(argoFilePath, builder.Workflow)
|
||||
} else {
|
||||
// Executed in a k8s environment
|
||||
fmt.Println("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, conf.GetConfig().ExecutionID, argoFilePath)
|
||||
}
|
||||
wf_exec := res.ToWorkflowExecution()
|
||||
|
||||
return wf_exec.WorkflowID
|
||||
}
|
||||
|
||||
// So far we only log the output from
|
||||
func executeInside(execID string, ns string, argo_file_path string) {
|
||||
t, err := tools2.NewService(conf.GetConfig().Mode)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not create KubernetesTool")
|
||||
return
|
||||
}
|
||||
|
||||
name, err := t.CreateArgoWorkflow(argo_file_path, ns)
|
||||
_ = name
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not create argo workflow : " + err.Error())
|
||||
fmt.Println("CA :" + conf.GetConfig().KubeCA)
|
||||
fmt.Println("Cert :" + conf.GetConfig().KubeCert)
|
||||
fmt.Println("Data :" + conf.GetConfig().KubeData)
|
||||
return
|
||||
} else {
|
||||
watcher, err := t.GetArgoWatch(execID, workflowName)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not retrieve Watcher : " + err.Error())
|
||||
}
|
||||
|
||||
func executeWorkflow(argo_file_path string, stepMax int) {
|
||||
// var stdout, stderr, stdout_logs, stderr_logs io.ReadCloser
|
||||
var stdout, stderr io.ReadCloser
|
||||
// var stderr io.ReadCloser
|
||||
l.LogKubernetesArgo(name, execID, watcher)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not log workflow : " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func executeOutside(argo_file_path string, workflow workflow_builder.Workflow) {
|
||||
var stdoutSubmit, stderrSubmit io.ReadCloser
|
||||
var stdoutLogs, stderrLogs io.ReadCloser
|
||||
var wg sync.WaitGroup
|
||||
var err error
|
||||
cmd := exec.Command("argo", "submit", "--log", "./argo_workflows/"+argo_file_path, "--serviceaccount=argo", "-n", "argo")
|
||||
fmt.Println(cmd)
|
||||
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())
|
||||
return
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
panic(err)
|
||||
|
||||
cmdLogs := exec.Command("argo", "logs", "oc-monitor-"+workflowName, "-n", conf.GetConfig().ExecutionID, "--follow","--no-color")
|
||||
if stdoutLogs, err = cmdLogs.StdoutPipe(); err != nil {
|
||||
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)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
go logWorkflow(argo_file_path, stepMax, stdout, &wg)
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
go l.LogLocalWorkflow(workflowName, stdoutSubmit, &wg)
|
||||
go l.LogLocalPod(workflowName, stdoutLogs, steps, &wg)
|
||||
|
||||
fmt.Println("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", "")
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
fmt.Println("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())
|
||||
|
||||
}
|
||||
|
||||
fmt.Println("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(stderr).Text())
|
||||
updateStatus("fatal")
|
||||
wf_logger.Error().Msg(err.Error() + bufio.NewScanner(stderrSubmit).Text())
|
||||
updateStatus("fatal", "")
|
||||
}
|
||||
|
||||
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, wg *sync.WaitGroup) {
|
||||
var current_watch, previous_watch *models.ArgoWatch
|
||||
split := strings.Split(argo_file_path, "_")
|
||||
argoLogs := models.NewArgoLogs(split[0], "argo", stepMax)
|
||||
|
||||
watch_output := make([]string, 0)
|
||||
scanner := bufio.NewScanner(pipe)
|
||||
for scanner.Scan() {
|
||||
log := scanner.Text()
|
||||
watch_output = append(watch_output, log)
|
||||
if strings.Contains(log, "Progress:") {
|
||||
current_watch = argoLogs.StopStepRecording(watch_output)
|
||||
watch_output = []string{}
|
||||
} else if strings.Contains(log, "sub-process exited") {
|
||||
current_watch = argoLogs.StopStepRecording(watch_output)
|
||||
}
|
||||
if current_watch != nil && !current_watch.Equals(previous_watch) && current_watch.Name != "" {
|
||||
wg.Add(1)
|
||||
checkStatus(current_watch, previous_watch)
|
||||
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{}
|
||||
watch_output = []string{}
|
||||
wg.Done()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadConfig(is_k8s bool, parser *argparse.Parser) {
|
||||
|
||||
var o *onion.Onion
|
||||
|
||||
o = initOnion(o)
|
||||
// These variables can only be retrieved in the onion
|
||||
// Variables that don't depend on the environment (from conf file), can be loaded after
|
||||
// We can't use underscore in the env variable names because it's the delimitor with OCMONITOR too
|
||||
setConf(is_k8s, o, parser)
|
||||
|
||||
if !IsValidUUID(conf.GetConfig().ExecutionID) {
|
||||
logger.Fatal().Msg("Provided ID is not an UUID")
|
||||
}
|
||||
// if !IsValidUUID(conf.GetConfig().ExecutionID) {
|
||||
// logger.Fatal().Msg("Provided ID is not an UUID")
|
||||
// }
|
||||
}
|
||||
|
||||
func setConf(is_k8s bool, o *onion.Onion, parser *argparse.Parser) {
|
||||
if is_k8s {
|
||||
conf.GetConfig().LokiURL = o.GetStringDefault("lokiurl", "http://127.0.0.1:3100")
|
||||
i, err := strconv.Atoi(o.GetString("timeout"))
|
||||
if err == nil {
|
||||
conf.GetConfig().Timeout = i
|
||||
} else {
|
||||
logger.Error().Msg("Could not parse timeout, using default value")
|
||||
}
|
||||
conf.GetConfig().ExecutionID = o.GetString("workflow")
|
||||
mongo := o.GetStringDefault("mongourl", "mongodb://127.0.0.1:27017")
|
||||
db := o.GetStringDefault("database", "DC_myDC")
|
||||
url := parser.String("u", "url", &argparse.Options{Required: true, Default: "http://127.0.0.1:3100", Help: "Url to the Loki database logs will be sent to"})
|
||||
mode := parser.String("M", "mode", &argparse.Options{Required: false, Default: "", Help: "Mode of the execution"})
|
||||
execution := parser.String("e", "execution", &argparse.Options{Required: true, Help: "Execution ID of the workflow to request from oc-catalog API"})
|
||||
peer := parser.String("p", "peer", &argparse.Options{Required: false, Default: "", Help: "Peer ID of the workflow to request from oc-catalog API"})
|
||||
mongo := parser.String("m", "mongo", &argparse.Options{Required: true, Default: "mongodb://127.0.0.1:27017", Help: "URL to reach the MongoDB"})
|
||||
db := parser.String("d", "database", &argparse.Options{Required: true, Default: "DC_myDC", Help: "Name of the database to query in MongoDB"})
|
||||
timeout := parser.Int("t", "timeout", &argparse.Options{Required: false, Default: -1, Help: "Timeout for the execution of the workflow"})
|
||||
|
||||
conf.GetConfig().MongoURL = mongo
|
||||
conf.GetConfig().Database = db
|
||||
} else {
|
||||
url := parser.String("u", "url", &argparse.Options{Required: true, Default: "http://127.0.0.1:3100", Help: "Url to the Loki database logs will be sent to"})
|
||||
execution := parser.String("e", "execution", &argparse.Options{Required: true, Help: "Execution ID of the workflow to request from oc-catalog API"})
|
||||
mongo := parser.String("m", "mongo", &argparse.Options{Required: true, Default: "mongodb://127.0.0.1:27017", Help: "URL to reach the MongoDB"})
|
||||
db := parser.String("d", "database", &argparse.Options{Required: true, Default: "DC_myDC", Help: "Name of the database to query in MongoDB"})
|
||||
timeout := parser.Int("t", "timeout", &argparse.Options{Required: false, Default: -1, Help: "Timeout for the execution of the workflow"})
|
||||
err := parser.Parse(os.Args)
|
||||
if err != nil {
|
||||
fmt.Println(parser.Usage(err))
|
||||
os.Exit(1)
|
||||
}
|
||||
ca := parser.String("c", "ca", &argparse.Options{Required: false, Default: "", Help: "CA file for the Kubernetes cluster"})
|
||||
cert := parser.String("C", "cert", &argparse.Options{Required: false, Default: "", Help: "Cert file for the Kubernetes cluster"})
|
||||
data := parser.String("D", "data", &argparse.Options{Required: false, Default: "", Help: "Data file for the Kubernetes cluster"})
|
||||
|
||||
conf.GetConfig().Logs = "debug"
|
||||
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"})
|
||||
|
||||
conf.GetConfig().LokiURL = *url
|
||||
conf.GetConfig().MongoURL = *mongo
|
||||
conf.GetConfig().Database = *db
|
||||
conf.GetConfig().Timeout = *timeout
|
||||
conf.GetConfig().ExecutionID = *execution
|
||||
// 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)
|
||||
if err != nil {
|
||||
fmt.Println(parser.Usage(err))
|
||||
os.Exit(1)
|
||||
}
|
||||
conf.GetConfig().Logs = "debug"
|
||||
conf.GetConfig().LokiURL = *url
|
||||
conf.GetConfig().MongoURL = *mongo
|
||||
conf.GetConfig().Database = *db
|
||||
conf.GetConfig().Timeout = *timeout
|
||||
conf.GetConfig().Mode = *mode
|
||||
conf.GetConfig().ExecutionID = *execution
|
||||
conf.GetConfig().PeerID = *peer
|
||||
|
||||
conf.GetConfig().KubeHost = *host
|
||||
conf.GetConfig().KubePort = *port
|
||||
|
||||
// conf.GetConfig().ArgoHost = *argoHost
|
||||
|
||||
decoded, err := base64.StdEncoding.DecodeString(*ca)
|
||||
if err == nil {
|
||||
conf.GetConfig().KubeCA = string(decoded)
|
||||
}
|
||||
decoded, err = base64.StdEncoding.DecodeString(*cert)
|
||||
if err == nil {
|
||||
conf.GetConfig().KubeCert = string(decoded)
|
||||
}
|
||||
decoded, err = base64.StdEncoding.DecodeString(*data)
|
||||
if err == nil {
|
||||
conf.GetConfig().KubeData = string(decoded)
|
||||
}
|
||||
}
|
||||
|
||||
func initOnion(o *onion.Onion) *onion.Onion {
|
||||
@@ -279,27 +300,47 @@ func getContainerName(argo_file string) string {
|
||||
re := regexp.MustCompile(regex)
|
||||
|
||||
container_name := re.FindString(argo_file)
|
||||
|
||||
return container_name
|
||||
}
|
||||
|
||||
// Uses the ArgoWatch object to update status of the workflow execution object
|
||||
func checkStatus(current *models.ArgoWatch, previous *models.ArgoWatch) {
|
||||
if previous != nil && current.Status != previous.Status {
|
||||
updateStatus(current.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func updateStatus(status string) {
|
||||
func updateStatus(status string, log string) {
|
||||
exec_id := conf.GetConfig().ExecutionID
|
||||
|
||||
wf_exec := &workflow_execution.WorkflowExecution{AbstractObject: utils.AbstractObject{UUID: conf.GetConfig().ExecutionID}}
|
||||
wf_exec.ArgoStatusToState(status)
|
||||
|
||||
serialized := wf_exec.Serialize()
|
||||
|
||||
res := oclib.UpdateOne(oclib.LibDataEnum(oclib.WORKFLOW_EXECUTION), serialized, exec_id)
|
||||
|
||||
if res.Code != 200 {
|
||||
logger.Error().Msg("Could not update status for workflow execution " + exec_id)
|
||||
exec, _, err := workflow_execution.NewAccessor(&tools.APIRequest{
|
||||
PeerID: conf.GetConfig().PeerID,
|
||||
}).UpdateOne(wf_exec, exec_id)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not update status for workflow execution " + exec_id + err.Error())
|
||||
}
|
||||
splitted := strings.Split(log, "-")
|
||||
if len(splitted) > 1 {
|
||||
we := exec.(*workflow_execution.WorkflowExecution)
|
||||
itemID := splitted[len(splitted)-1] // TODO: in logs found item ID
|
||||
caller := &tools.HTTPCaller{
|
||||
URLS: map[tools.DataType]map[tools.METHOD]string{
|
||||
tools.PEER: {
|
||||
tools.POST: "/status/",
|
||||
},
|
||||
tools.BOOKING: {
|
||||
tools.PUT: "http://localhost:8080/booking/:id",
|
||||
},
|
||||
},
|
||||
}
|
||||
if we.PeerBookByGraph != nil {
|
||||
for peerID, val := range we.PeerBookByGraph {
|
||||
if val[itemID] == nil {
|
||||
continue
|
||||
}
|
||||
for _, log := range val[itemID] {
|
||||
(&peer.Peer{}).LaunchPeerExecution(peerID, log, tools.BOOKING, tools.PUT, &booking.Booking{
|
||||
State: we.State,
|
||||
}, caller)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/acarl005/stripansi"
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
type ArgoLogs struct {
|
||||
Name string
|
||||
Namespace string
|
||||
CreatedDate string
|
||||
StepCount int
|
||||
StepMax int
|
||||
stop bool
|
||||
Started time.Time
|
||||
}
|
||||
|
||||
func (a *ArgoLogs) StartStepRecording() {
|
||||
a.StepCount += 1
|
||||
a.Started = time.Now()
|
||||
}
|
||||
|
||||
func (a *ArgoLogs) StopStepRecording(inputs []string) *ArgoWatch {
|
||||
fn := strings.Split(a.Name, "_")
|
||||
logs := []string{}
|
||||
err := false
|
||||
end := ""
|
||||
for _, input := range inputs {
|
||||
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()
|
||||
}
|
||||
argo := &ArgoWatch{
|
||||
Name: a.Name,
|
||||
Namespace: a.Namespace,
|
||||
Status: status,
|
||||
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: fmt.Sprintf("%v", fmt.Sprintf("%.2f", duration)+"s"),
|
||||
Logs: logs,
|
||||
}
|
||||
if !argo.Completed {
|
||||
a.StartStepRecording()
|
||||
}
|
||||
return argo
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package models
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"cloud.o-forge.io/core/oc-lib/models/resources/processing"
|
||||
"cloud.o-forge.io/core/oc-lib/models/resources/storage"
|
||||
"cloud.o-forge.io/core/oc-lib/models/common/models"
|
||||
"cloud.o-forge.io/core/oc-lib/models/resources"
|
||||
)
|
||||
|
||||
type Parameter struct {
|
||||
@@ -38,9 +38,9 @@ func (c *Container) AddVolumeMount(volumeMount VolumeMount, volumes []VolumeMoun
|
||||
}
|
||||
|
||||
type VolumeMount struct {
|
||||
Name string `yaml:"name"`
|
||||
MountPath string `yaml:"mountPath"`
|
||||
Storage *storage.StorageResource `yaml:"-"`
|
||||
Name string `yaml:"name"`
|
||||
MountPath string `yaml:"mountPath"`
|
||||
Storage *resources.StorageResource `yaml:"-"`
|
||||
}
|
||||
|
||||
type Task struct {
|
||||
@@ -58,54 +58,95 @@ type Dag struct {
|
||||
|
||||
type TemplateMetadata struct {
|
||||
Labels map[string]string `yaml:"labels,omitempty"`
|
||||
Annotations map[string]string `yaml:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
Name string `yaml:"name"`
|
||||
Key string `yaml:"key"`
|
||||
}
|
||||
|
||||
type Key struct {
|
||||
Key string `yaml:"key"`
|
||||
Bucket string `yaml:"bucket"`
|
||||
EndPoint string `yaml:"endpoint"`
|
||||
Insecure bool `yaml:"insecure"`
|
||||
AccessKeySecret *Secret `yaml accessKeySecret`
|
||||
SecretKeySecret *Secret `yaml secretKeySecret`
|
||||
}
|
||||
|
||||
type Artifact struct {
|
||||
Name string `yaml:"name"`
|
||||
Path string `yaml:"path"`
|
||||
S3 *Key `yaml:"s3,omitempty"`
|
||||
}
|
||||
|
||||
type InOut struct {
|
||||
Parameters []Parameter `yaml:"parameters"`
|
||||
Artifacts []Artifact `yaml:"artifacts,omitempty"`
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
Name string `yaml:"name"`
|
||||
Inputs struct {
|
||||
Parameters []Parameter `yaml:"parameters"`
|
||||
} `yaml:"inputs,omitempty"`
|
||||
Name string `yaml:"name"`
|
||||
Inputs InOut `yaml:"inputs,omitempty"`
|
||||
Outputs InOut `yaml:"outputs,omitempty"`
|
||||
Container Container `yaml:"container,omitempty"`
|
||||
Dag *Dag `yaml:"dag,omitempty"`
|
||||
Metadata TemplateMetadata `yaml:"metadata,omitempty"`
|
||||
Resource ServiceResource `yaml:"resource,omitempty"`
|
||||
}
|
||||
|
||||
func (template *Template) CreateContainer(processing *processing.ProcessingResource, dag *Dag) {
|
||||
container := Container{Image: processing.Container.Image}
|
||||
func (template *Template) CreateContainer(processing *resources.ProcessingResource, dag *Dag) {
|
||||
instance := processing.GetSelectedInstance()
|
||||
if instance == nil {
|
||||
return
|
||||
}
|
||||
inst := instance.(*resources.ProcessingInstance)
|
||||
container := Container{Image: inst.Access.Container.Image}
|
||||
if container.Image == "" {
|
||||
return
|
||||
}
|
||||
container.Command = []string{"sh", "-c"} // all is bash
|
||||
for name := range processing.Container.Env {
|
||||
template.Inputs.Parameters = append(template.Inputs.Parameters, Parameter{Name: name})
|
||||
for _, v := range inst.Env {
|
||||
template.Inputs.Parameters = append(template.Inputs.Parameters, Parameter{Name: v.Name})
|
||||
}
|
||||
for _, a := range strings.Split(processing.Container.Args, " ") {
|
||||
container.Args = append(container.Args, template.replacePerEnv(a, processing.Container.Env, dag))
|
||||
for _, v := range inst.Inputs {
|
||||
template.Inputs.Parameters = append(template.Inputs.Parameters, Parameter{Name: v.Name})
|
||||
}
|
||||
cmd := strings.ReplaceAll(processing.Container.Command, container.Image, "")
|
||||
container.Args = []string{cmd + " " + strings.Join(container.Args, " ")}
|
||||
for _, v := range inst.Inputs {
|
||||
template.Outputs.Parameters = append(template.Inputs.Parameters, Parameter{Name: v.Name})
|
||||
}
|
||||
cmd := strings.ReplaceAll(inst.Access.Container.Command, container.Image, "")
|
||||
|
||||
for _, a := range strings.Split(cmd, " ") {
|
||||
container.Args = append(container.Args, template.ReplacePerEnv(a, inst.Env))
|
||||
}
|
||||
for _, a := range strings.Split(inst.Access.Container.Args, " ") {
|
||||
container.Args = append(container.Args, template.ReplacePerEnv(a, inst.Env))
|
||||
}
|
||||
container.Args = []string{strings.Join(container.Args, " ")}
|
||||
|
||||
template.Container = container
|
||||
}
|
||||
|
||||
func (template *Template) replacePerEnv(arg string, envs map[string]string, dag *Dag) string {
|
||||
for k, v := range envs {
|
||||
if strings.Contains(arg, k) {
|
||||
value := v
|
||||
for _, task := range dag.Tasks {
|
||||
if task.Name == template.Name {
|
||||
for _, p := range task.Arguments.Parameters {
|
||||
if p.Name == k {
|
||||
value = p.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
arg = strings.ReplaceAll(arg, "$"+k, value)
|
||||
arg = strings.ReplaceAll(arg, "${"+k+"}", value)
|
||||
arg = strings.ReplaceAll(arg, k, value)
|
||||
func (template *Template) ReplacePerEnv(arg string, envs []models.Param) string {
|
||||
for _, v := range envs {
|
||||
if strings.Contains(arg, 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, "$", "")
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
27
tools/interface.go
Normal file
27
tools/interface.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
type Tool interface {
|
||||
CreateArgoWorkflow(path string, ns string) (string, error)
|
||||
CreateAccessSecret(ns string, login string, password string) (string, error)
|
||||
GetArgoWatch(executionId string, wfName string) (watch.Interface, error)
|
||||
GetPodLogger(ns string, wfName string, podName string) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
var _service = map[string]func() (Tool, error){
|
||||
"kubernetes": NewKubernetesTool,
|
||||
}
|
||||
|
||||
func NewService(name string) (Tool, error) {
|
||||
service, ok := _service[name]
|
||||
if !ok {
|
||||
return nil, errors.New("service not found")
|
||||
}
|
||||
return service()
|
||||
}
|
||||
187
tools/kubernetes.go
Normal file
187
tools/kubernetes.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"oc-monitord/conf"
|
||||
"oc-monitord/utils"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
|
||||
"github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned"
|
||||
"github.com/google/uuid"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type KubernetesTools struct {
|
||||
Set *kubernetes.Clientset
|
||||
VersionedSet *versioned.Clientset
|
||||
}
|
||||
|
||||
func NewKubernetesTool() (Tool, error) {
|
||||
// Load Kubernetes config (from ~/.kube/config)
|
||||
config := &rest.Config{
|
||||
Host: conf.GetConfig().KubeHost + ":" + conf.GetConfig().KubePort,
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
CAData: []byte(conf.GetConfig().KubeCA),
|
||||
CertData: []byte(conf.GetConfig().KubeCert),
|
||||
KeyData: []byte(conf.GetConfig().KubeData),
|
||||
},
|
||||
}
|
||||
|
||||
// Create clientset
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, errors.New("Error creating Kubernetes client: " + err.Error())
|
||||
}
|
||||
|
||||
clientset2, err := versioned.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, errors.New("Error creating Kubernetes versionned client: " + err.Error())
|
||||
}
|
||||
|
||||
return &KubernetesTools{
|
||||
Set: clientset,
|
||||
VersionedSet: clientset2,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (k *KubernetesTools) CreateArgoWorkflow(path string, ns string) (string, error) {
|
||||
// Read workflow YAML file
|
||||
workflowYAML, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Decode the YAML into a Workflow struct
|
||||
scheme := runtime.NewScheme()
|
||||
_ = wfv1.AddToScheme(scheme)
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
decode := codecs.UniversalDeserializer().Decode
|
||||
|
||||
obj, _, err := decode(workflowYAML, nil, nil)
|
||||
if err != nil {
|
||||
return "", errors.New("failed to decode YAML: " + err.Error())
|
||||
}
|
||||
|
||||
workflow, ok := obj.(*wfv1.Workflow)
|
||||
if !ok {
|
||||
return "", errors.New("decoded object is not a Workflow")
|
||||
}
|
||||
|
||||
// Create the workflow in the "argo" namespace
|
||||
createdWf, err := k.VersionedSet.ArgoprojV1alpha1().Workflows(ns).Create(context.TODO(), workflow, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return "", errors.New("failed to create workflow: " + err.Error())
|
||||
}
|
||||
fmt.Printf("workflow %s created in namespace %s\n", createdWf.Name, ns)
|
||||
return createdWf.Name, nil
|
||||
}
|
||||
|
||||
func (k *KubernetesTools) CreateAccessSecret(ns string, login string, password string) (string, error) {
|
||||
// Namespace where the secret will be created
|
||||
namespace := "default"
|
||||
// Encode the secret data (Kubernetes requires base64-encoded values)
|
||||
secretData := map[string][]byte{
|
||||
"access-key": []byte(base64.StdEncoding.EncodeToString([]byte(login))),
|
||||
"secret-key": []byte(base64.StdEncoding.EncodeToString([]byte(password))),
|
||||
}
|
||||
|
||||
// Define the Secret object
|
||||
name := uuid.New().String()
|
||||
secret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: ns,
|
||||
},
|
||||
Type: v1.SecretTypeOpaque,
|
||||
Data: secretData,
|
||||
}
|
||||
// Create the Secret in Kubernetes
|
||||
_, err := k.Set.CoreV1().Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return "", errors.New("Error creating secret: " + err.Error())
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (k *KubernetesTools) GetArgoWatch(executionId string, wfName string) (watch.Interface, error){
|
||||
wfl := utils.GetWFLogger("")
|
||||
wfl.Debug().Msg("Starting argo watch with argo lib")
|
||||
fmt.Println("metadata.name=oc-monitor-"+wfName + " in namespace : " + executionId)
|
||||
options := metav1.ListOptions{FieldSelector: "metadata.name=oc-monitor-"+wfName}
|
||||
fmt.Println(options)
|
||||
watcher, err := k.VersionedSet.ArgoprojV1alpha1().Workflows(executionId).Watch(context.TODO(), 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/node-name=" + nodeName)
|
||||
}
|
||||
|
||||
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 {
|
||||
if cond.Type == v1.PodReady && cond.Status == v1.ConditionTrue {
|
||||
initialized = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if initialized {
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second) // avoid hammering the API
|
||||
}
|
||||
}
|
||||
45
utils/utils.go
Normal file
45
utils/utils.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"oc-monitord/conf"
|
||||
"sync"
|
||||
|
||||
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"
|
||||
"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 {
|
||||
res := oclib.NewRequest(oclib.LibDataEnum(oclib.WORKFLOW_EXECUTION), "", conf.GetConfig().PeerID, []string{}, nil).LoadOne(exec_id)
|
||||
if res.Code != 200 {
|
||||
logger := oclib.GetLogger()
|
||||
logger.Error().Msg("Could not retrieve workflow ID from execution ID " + exec_id)
|
||||
return nil
|
||||
}
|
||||
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
|
||||
}
|
||||
143
workflow_builder/admiralty_setter.go
Normal file
143
workflow_builder/admiralty_setter.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package workflow_builder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
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"
|
||||
tools "cloud.o-forge.io/core/oc-lib/tools"
|
||||
)
|
||||
|
||||
|
||||
type AdmiraltySetter struct {
|
||||
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 {
|
||||
|
||||
logger = logs.GetLogger()
|
||||
|
||||
data := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER),"",localPeerID,nil,nil).LoadOne(remotePeerID)
|
||||
if data.Code != 200 {
|
||||
logger.Error().Msg("Error while trying to instantiate remote peer " + remotePeerID)
|
||||
return fmt.Errorf(data.Err)
|
||||
}
|
||||
remotePeer := data.ToPeer()
|
||||
|
||||
data = oclib.NewRequest(oclib.LibDataEnum(oclib.PEER),"",localPeerID,nil,nil).LoadOne(localPeerID)
|
||||
if data.Code != 200 {
|
||||
logger.Error().Msg("Error while trying to instantiate local peer " + remotePeerID)
|
||||
return fmt.Errorf(data.Err)
|
||||
}
|
||||
localPeer := data.ToPeer()
|
||||
|
||||
caller := tools.NewHTTPCaller(
|
||||
map[tools.DataType]map[tools.METHOD]string{
|
||||
tools.ADMIRALTY_SOURCE: map[tools.METHOD]string{
|
||||
tools.POST :"/:id",
|
||||
},
|
||||
tools.ADMIRALTY_KUBECONFIG: map[tools.METHOD]string{
|
||||
tools.GET:"/:id",
|
||||
},
|
||||
tools.ADMIRALTY_SECRET: map[tools.METHOD]string{
|
||||
tools.POST:"/:id",
|
||||
},
|
||||
tools.ADMIRALTY_TARGET: map[tools.METHOD]string{
|
||||
tools.POST:"/:id",
|
||||
},
|
||||
tools.ADMIRALTY_NODES: map[tools.METHOD]string{
|
||||
tools.GET:"/:id",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
logger.Info().Msg(" Creating the Admiralty Source on " + remotePeerID + " ns-" + s.Id + "\n\n")
|
||||
_ = 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")
|
||||
kubeconfig := s.getKubeconfig(remotePeer, caller)
|
||||
logger.Info().Msg(" Creating a secret from the kubeconfig " + localPeerID + " ns-" + s.Id + "\n\n")
|
||||
_ = 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")
|
||||
_ = 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")
|
||||
s.checkNodeStatus(localPeer,caller)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AdmiraltySetter) getKubeconfig(peer *peer.Peer, caller *tools.HTTPCaller) map[string]string {
|
||||
var kubedata map[string]string
|
||||
_ = 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 {
|
||||
fmt.Println("Something went wrong when retrieving data from Get call for kubeconfig")
|
||||
panic(0)
|
||||
}
|
||||
err := json.Unmarshal(caller.LastResults["body"].([]byte), &kubedata)
|
||||
if err != nil {
|
||||
fmt.Println("Something went wrong when unmarshalling data from Get call for kubeconfig")
|
||||
panic(0)
|
||||
}
|
||||
|
||||
return kubedata
|
||||
}
|
||||
|
||||
func (*AdmiraltySetter) callRemoteExecution(peer *peer.Peer, expectedCode []int,caller *tools.HTTPCaller, dataID string, dt tools.DataType, method tools.METHOD, body interface{}, panicCode bool) *peer.PeerExecution {
|
||||
resp, err := peer.LaunchPeerExecution(peer.UUID, dataID, dt, method, body, caller)
|
||||
if err != nil {
|
||||
fmt.Println("Error when executing on peer at", peer.Url)
|
||||
fmt.Println(err)
|
||||
panic(0)
|
||||
}
|
||||
|
||||
if !slices.Contains(expectedCode, caller.LastResults["code"].(int)) {
|
||||
fmt.Println("Didn't receive the expected code :", caller.LastResults["code"], "when expecting", expectedCode)
|
||||
if _, ok := caller.LastResults["body"]; ok {
|
||||
logger.Info().Msg(string(caller.LastResults["body"].([]byte)))
|
||||
// fmt.Println(string(caller.LastResults["body"].([]byte)))
|
||||
}
|
||||
if panicCode {
|
||||
panic(0)
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
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 {
|
||||
fmt.Println("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(5 * 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...")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,15 +6,17 @@ package workflow_builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"oc-monitord/conf"
|
||||
. "oc-monitord/models"
|
||||
tools2 "oc-monitord/tools"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
oclib "cloud.o-forge.io/core/oc-lib"
|
||||
"cloud.o-forge.io/core/oc-lib/models/resources/processing"
|
||||
"cloud.o-forge.io/core/oc-lib/models/resources/workflow/graph"
|
||||
"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/resources"
|
||||
w "cloud.o-forge.io/core/oc-lib/models/workflow"
|
||||
"github.com/nwtgck/go-fakelish"
|
||||
"github.com/rs/zerolog"
|
||||
@@ -24,10 +26,11 @@ import (
|
||||
var logger zerolog.Logger
|
||||
|
||||
type ArgoBuilder struct {
|
||||
OriginWorkflow *w.Workflow
|
||||
Workflow Workflow
|
||||
Services []*Service
|
||||
Timeout int
|
||||
OriginWorkflow *w.Workflow
|
||||
Workflow Workflow
|
||||
Services []*Service
|
||||
Timeout int
|
||||
RemotePeers []string
|
||||
}
|
||||
|
||||
type Workflow struct {
|
||||
@@ -39,14 +42,6 @@ type Workflow struct {
|
||||
Spec Spec `yaml:"spec,omitempty"`
|
||||
}
|
||||
|
||||
func (b *Workflow) setDag(dag *Dag) {
|
||||
for _, t := range b.Spec.Templates {
|
||||
if t.Name == "dag" {
|
||||
t.Dag = dag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Workflow) getDag() *Dag {
|
||||
for _, t := range b.Spec.Templates {
|
||||
if t.Name == "dag" {
|
||||
@@ -58,80 +53,73 @@ func (b *Workflow) getDag() *Dag {
|
||||
}
|
||||
|
||||
type Spec struct {
|
||||
Entrypoint string `yaml:"entrypoint"`
|
||||
Arguments []Parameter `yaml:"arguments,omitempty"`
|
||||
Volumes []VolumeClaimTemplate `yaml:"volumeClaimTemplates,omitempty"`
|
||||
Templates []Template `yaml:"templates"`
|
||||
Timeout int `yaml:"activeDeadlineSeconds,omitempty"`
|
||||
ServiceAccountName string `yaml:"serviceAccountName"`
|
||||
Entrypoint string `yaml:"entrypoint"`
|
||||
Arguments []Parameter `yaml:"arguments,omitempty"`
|
||||
Volumes []VolumeClaimTemplate `yaml:"volumeClaimTemplates,omitempty"`
|
||||
Templates []Template `yaml:"templates"`
|
||||
Timeout int `yaml:"activeDeadlineSeconds,omitempty"`
|
||||
}
|
||||
|
||||
func (b *ArgoBuilder) CreateDAG(write bool) (string, int, []string, []string, error) {
|
||||
|
||||
// TODO: found on a processing instance linked to storage
|
||||
// add s3, gcs, azure, etc if needed on a link between processing and storage
|
||||
func (b *ArgoBuilder) CreateDAG(namespace string, write bool) ( int, []string, []string, error) {
|
||||
logger = logs.GetLogger()
|
||||
fmt.Println("Creating DAG", b.OriginWorkflow.Graph.Items)
|
||||
// handle services by checking if there is only one processing with hostname and port
|
||||
firstItems, lastItems, volumes := b.createTemplates()
|
||||
firstItems, lastItems, volumes := b.createTemplates(namespace)
|
||||
b.createVolumes(volumes)
|
||||
|
||||
if b.Timeout > 0 {
|
||||
b.Workflow.Spec.Timeout = b.Timeout
|
||||
}
|
||||
b.Workflow.Spec.ServiceAccountName = "sa-"+namespace
|
||||
b.Workflow.Spec.Entrypoint = "dag"
|
||||
b.Workflow.ApiVersion = "argoproj.io/v1alpha1"
|
||||
b.Workflow.Kind = "Workflow"
|
||||
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()
|
||||
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 file_name, len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
|
||||
|
||||
|
||||
return len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
|
||||
}
|
||||
|
||||
func (b *ArgoBuilder) createTemplates() ([]string, []string, []VolumeMount) {
|
||||
func (b *ArgoBuilder) createTemplates(namespace string) ([]string, []string, []VolumeMount) {
|
||||
volumes := []VolumeMount{}
|
||||
firstItems := []string{}
|
||||
lastItems := []string{}
|
||||
for _, comp := range b.OriginWorkflow.GetProcessings() {
|
||||
if comp.Processing.Container != nil {
|
||||
volumes, firstItems, lastItems = b.createArgoTemplates(
|
||||
comp.ID, comp.Processing, volumes, firstItems, lastItems)
|
||||
} else {
|
||||
logger.Error().Msg("Not enough configuration setup, template can't be created : " + comp.Processing.GetName())
|
||||
items := b.OriginWorkflow.GetGraphItems(b.OriginWorkflow.Graph.IsProcessing)
|
||||
fmt.Println("Creating templates", len(items))
|
||||
for _, item := range b.OriginWorkflow.GetGraphItems(b.OriginWorkflow.Graph.IsProcessing) {
|
||||
instance := item.Processing.GetSelectedInstance()
|
||||
fmt.Println("Creating template for", item.Processing.GetName(), instance)
|
||||
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())
|
||||
return firstItems, lastItems, volumes
|
||||
}
|
||||
volumes, firstItems, lastItems = b.createArgoTemplates(namespace,
|
||||
item.ID, item.Processing, volumes, firstItems, lastItems)
|
||||
}
|
||||
firstWfTasks := map[string][]string{}
|
||||
latestWfTasks := map[string][]string{}
|
||||
relatedWfTasks := map[string][]string{}
|
||||
for _, wf := range b.OriginWorkflow.GetWorkflows() {
|
||||
realWorkflow, code, err := w.New().LoadOne(wf.Workflow.WorkflowID)
|
||||
for _, wf := range b.OriginWorkflow.Workflows {
|
||||
realWorkflow, code, err := w.NewAccessor(nil).LoadOne(wf)
|
||||
if code != 200 {
|
||||
logger.Error().Msg("Error loading the workflow : " + err.Error())
|
||||
continue
|
||||
}
|
||||
subBuilder := ArgoBuilder{OriginWorkflow: realWorkflow.(*w.Workflow), Timeout: b.Timeout}
|
||||
_, _, fi, li, err := subBuilder.CreateDAG(false)
|
||||
_, fi, li, err := subBuilder.CreateDAG(namespace, false)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Error creating the subworkflow : " + err.Error())
|
||||
continue
|
||||
}
|
||||
firstWfTasks[wf.ID] = fi
|
||||
if ok, depsOfIds := subBuilder.isArgoDependancy(wf.ID); ok { // IS BEFORE
|
||||
latestWfTasks[wf.ID] = li
|
||||
relatedWfTasks[wf.ID] = depsOfIds
|
||||
firstWfTasks[wf] = fi
|
||||
if ok, depsOfIds := subBuilder.isArgoDependancy(wf); ok { // IS BEFORE
|
||||
latestWfTasks[wf] = li
|
||||
relatedWfTasks[wf] = depsOfIds
|
||||
}
|
||||
subDag := subBuilder.Workflow.getDag()
|
||||
d := b.Workflow.getDag()
|
||||
@@ -180,26 +168,92 @@ func (b *ArgoBuilder) createTemplates() ([]string, []string, []VolumeMount) {
|
||||
return firstItems, lastItems, volumes
|
||||
}
|
||||
|
||||
func (b *ArgoBuilder) createArgoTemplates(id string,
|
||||
processing *processing.ProcessingResource,
|
||||
func (b *ArgoBuilder) createArgoTemplates(namespace string,
|
||||
id string,
|
||||
processing *resources.ProcessingResource,
|
||||
volumes []VolumeMount,
|
||||
firstItems []string,
|
||||
lastItems []string) ([]VolumeMount, []string, []string) {
|
||||
_, firstItems, lastItems = b.addTaskToArgo(b.Workflow.getDag(), id, processing, firstItems, lastItems)
|
||||
template := &Template{Name: getArgoName(processing.GetName(), id)}
|
||||
fmt.Println("Creating template for", template.Name)
|
||||
isReparted, peerId := b.isProcessingReparted(*processing,id)
|
||||
template.CreateContainer(processing, b.Workflow.getDag())
|
||||
if isReparted {
|
||||
b.RemotePeers = append(b.RemotePeers, peerId)
|
||||
template.AddAdmiraltyAnnotations(peerId)
|
||||
}
|
||||
// get datacenter from the processing
|
||||
if processing.IsService {
|
||||
b.CreateService(id, processing)
|
||||
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
|
||||
}
|
||||
storages := b.OriginWorkflow.GetStoragesByRelatedProcessing(id)
|
||||
for _, storage := range storages {
|
||||
if storage.Local {
|
||||
related := b.OriginWorkflow.GetByRelatedProcessing(id, b.OriginWorkflow.Graph.IsStorage)
|
||||
for _, r := range related {
|
||||
storage := r.Node.(*resources.StorageResource)
|
||||
for _, linkToStorage := range r.Links {
|
||||
for _, rw := range linkToStorage.StorageLinkInfos {
|
||||
art := Artifact{Path: template.ReplacePerEnv(rw.Source, linkToStorage.Env)}
|
||||
if rw.Write {
|
||||
art.Name = storage.GetName() + "-" + rw.Destination + "-input-write"
|
||||
} else {
|
||||
art.Name = storage.GetName() + "-" + rw.Destination + "-input-read"
|
||||
}
|
||||
if storage.StorageType == enum.S3 {
|
||||
art.S3 = &Key{
|
||||
Key: template.ReplacePerEnv(rw.Destination+"/"+rw.FileName, linkToStorage.Env),
|
||||
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 {
|
||||
template.Outputs.Artifacts = append(template.Inputs.Artifacts, art)
|
||||
} else {
|
||||
template.Inputs.Artifacts = append(template.Outputs.Artifacts, art)
|
||||
}
|
||||
}
|
||||
}
|
||||
index := 0
|
||||
if storage.SelectedInstanceIndex != nil && (*storage.SelectedInstanceIndex) >= 0 {
|
||||
index = *storage.SelectedInstanceIndex
|
||||
}
|
||||
s := storage.Instances[index]
|
||||
if s.Local {
|
||||
volumes = template.Container.AddVolumeMount(VolumeMount{
|
||||
Name: strings.ReplaceAll(strings.ToLower(storage.GetName()), " ", "-"),
|
||||
MountPath: storage.Path,
|
||||
MountPath: s.Source,
|
||||
Storage: storage,
|
||||
}, volumes)
|
||||
}
|
||||
@@ -207,15 +261,29 @@ func (b *ArgoBuilder) createArgoTemplates(id string,
|
||||
b.Workflow.Spec.Templates = append(b.Workflow.Spec.Templates, *template)
|
||||
return volumes, firstItems, lastItems
|
||||
}
|
||||
func (b *ArgoBuilder) addTaskToArgo(dag *Dag, graphItemID string, processing *processing.ProcessingResource,
|
||||
|
||||
func (b *ArgoBuilder) addTaskToArgo(dag *Dag, graphItemID string, processing *resources.ProcessingResource,
|
||||
firstItems []string, lastItems []string) (*Dag, []string, []string) {
|
||||
unique_name := getArgoName(processing.GetName(), graphItemID)
|
||||
step := Task{Name: unique_name, Template: unique_name}
|
||||
if processing.Container != nil {
|
||||
for name, value := range processing.Container.Env {
|
||||
instance := processing.GetSelectedInstance()
|
||||
if instance != nil {
|
||||
for _, value := range instance.(*resources.ProcessingInstance).Env {
|
||||
step.Arguments.Parameters = append(step.Arguments.Parameters, Parameter{
|
||||
Name: name,
|
||||
Value: b.affectVariableEnv(value, b.OriginWorkflow.Graph),
|
||||
Name: value.Name,
|
||||
Value: value.Value,
|
||||
})
|
||||
}
|
||||
for _, value := range instance.(*resources.ProcessingInstance).Inputs {
|
||||
step.Arguments.Parameters = append(step.Arguments.Parameters, Parameter{
|
||||
Name: value.Name,
|
||||
Value: value.Value,
|
||||
})
|
||||
}
|
||||
for _, value := range instance.(*resources.ProcessingInstance).Outputs {
|
||||
step.Arguments.Parameters = append(step.Arguments.Parameters, Parameter{
|
||||
Name: value.Name,
|
||||
Value: value.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -237,32 +305,17 @@ func (b *ArgoBuilder) addTaskToArgo(dag *Dag, graphItemID string, processing *pr
|
||||
return dag, firstItems, lastItems
|
||||
}
|
||||
|
||||
func (b *ArgoBuilder) affectVariableEnv(envVar string, graph *graph.Graph) string {
|
||||
var myExp = regexp.MustCompile(`(\{\{.*\}\})`) // regex to find all the variables in the command
|
||||
matches := myExp.FindAllString(envVar, -1) // find all the variables in the command
|
||||
for _, match := range matches { // for each variable in the command
|
||||
splitted := strings.Split( // split the variable to get the inout and the vars only
|
||||
strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(match, "{{", ""), "}}", ""), " ", ""), "_")
|
||||
if len(splitted) < 3 { // if the variable is not well formatted, we skip it
|
||||
logger.Error().Msgf("The variable %v is not well formatted", match)
|
||||
continue
|
||||
}
|
||||
graphItemID := splitted[1] // graphitemid is the id of the object
|
||||
vars := splitted[2] // vars is the name of the variable of the object
|
||||
_, obj := graph.GetResource(graphItemID)
|
||||
if obj != nil {
|
||||
envVar = strings.ReplaceAll(envVar, match, fmt.Sprintf("%v", obj.Serialize()[vars]))
|
||||
}
|
||||
}
|
||||
return envVar
|
||||
}
|
||||
|
||||
func (b *ArgoBuilder) createVolumes(volumes []VolumeMount) { // TODO : one think about remote volume but TG
|
||||
for _, volume := range volumes {
|
||||
index := 0
|
||||
if volume.Storage.SelectedInstanceIndex != nil && (*volume.Storage.SelectedInstanceIndex) >= 0 {
|
||||
index = *volume.Storage.SelectedInstanceIndex
|
||||
}
|
||||
storage := volume.Storage.Instances[index]
|
||||
new_volume := VolumeClaimTemplate{}
|
||||
new_volume.Metadata.Name = strings.ReplaceAll(strings.ToLower(volume.Name), " ", "-")
|
||||
new_volume.Spec.AccessModes = []string{"ReadWriteOnce"}
|
||||
new_volume.Spec.Resources.Requests.Storage = fmt.Sprintf("%v", volume.Storage.Size) + volume.Storage.SizeType.ToArgo()
|
||||
new_volume.Spec.Resources.Requests.Storage = fmt.Sprintf("%v", storage.SizeGB) + storage.SizeType.ToArgo()
|
||||
b.Workflow.Spec.Volumes = append(b.Workflow.Spec.Volumes, new_volume)
|
||||
}
|
||||
}
|
||||
@@ -271,6 +324,10 @@ func (b *ArgoBuilder) isArgoDependancy(id string) (bool, []string) {
|
||||
dependancyOfIDs := []string{}
|
||||
isDeps := false
|
||||
for _, link := range b.OriginWorkflow.Graph.Links {
|
||||
if _, ok := b.OriginWorkflow.Graph.Items[link.Destination.ID]; !ok {
|
||||
fmt.Println("Could not find the source of the link", link.Destination.ID)
|
||||
continue
|
||||
}
|
||||
source := b.OriginWorkflow.Graph.Items[link.Destination.ID].Processing
|
||||
if id == link.Source.ID && source != nil {
|
||||
isDeps = true
|
||||
@@ -287,6 +344,10 @@ func (b *ArgoBuilder) isArgoDependancy(id string) (bool, []string) {
|
||||
|
||||
func (b *ArgoBuilder) getArgoDependencies(id string) (dependencies []string) {
|
||||
for _, link := range b.OriginWorkflow.Graph.Links {
|
||||
if _, ok := b.OriginWorkflow.Graph.Items[link.Source.ID]; !ok {
|
||||
fmt.Println("Could not find the source of the link", link.Source.ID)
|
||||
continue
|
||||
}
|
||||
source := b.OriginWorkflow.Graph.Items[link.Source.ID].Processing
|
||||
if id == link.Destination.ID && source != nil {
|
||||
dependency_name := getArgoName(source.GetName(), link.Source.ID)
|
||||
@@ -303,3 +364,103 @@ func getArgoName(raw_name string, component_id string) (formatedName string) {
|
||||
formatedName = strings.ToLower(formatedName)
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
func (b *ArgoBuilder) isProcessingReparted(processing resources.ProcessingResource, graphID string) (bool,string) {
|
||||
computeAttached := b.retrieveProcessingCompute(graphID)
|
||||
if computeAttached == nil {
|
||||
logger.Error().Msg("No compute was found attached to processing " + processing.Name + " : " + processing.UUID )
|
||||
panic(0)
|
||||
}
|
||||
|
||||
|
||||
// Creates an accessor srtictly for Peer Collection
|
||||
req := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER),"","",nil,nil)
|
||||
if req == nil {
|
||||
fmt.Println("TODO : handle error when trying to create a request on the Peer Collection")
|
||||
return false, ""
|
||||
}
|
||||
|
||||
res := req.LoadOne(computeAttached.CreatorID)
|
||||
if res.Err != "" {
|
||||
fmt.Print("TODO : handle error when requesting PeerID")
|
||||
fmt.Print(res.Err)
|
||||
return false, ""
|
||||
}
|
||||
|
||||
peer := *res.ToPeer()
|
||||
|
||||
isNotReparted, _ := peer.IsMySelf()
|
||||
fmt.Println("Result IsMySelf for ", peer.UUID ," : ", isNotReparted)
|
||||
|
||||
return !isNotReparted, peer.UUID
|
||||
}
|
||||
|
||||
func (b *ArgoBuilder) retrieveProcessingCompute(graphID string) *resources.ComputeResource {
|
||||
for _, link := range b.OriginWorkflow.Graph.Links {
|
||||
// If a link contains the id of the processing
|
||||
var oppositeId string
|
||||
if link.Source.ID == graphID{
|
||||
oppositeId = link.Destination.ID
|
||||
} else if(link.Destination.ID == graphID){
|
||||
oppositeId = link.Source.ID
|
||||
}
|
||||
fmt.Println("OppositeId : ", oppositeId)
|
||||
if oppositeId != "" {
|
||||
dt, res := b.OriginWorkflow.Graph.GetResource(oppositeId)
|
||||
if dt == oclib.COMPUTE_RESOURCE {
|
||||
return res.(*resources.ComputeResource)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// Execute the last actions once the YAML file for the Argo Workflow is created
|
||||
func (b *ArgoBuilder) CompleteBuild(executionsId string) (string, error) {
|
||||
fmt.Println("DEV :: Completing build")
|
||||
setter := AdmiraltySetter{Id: executionsId}
|
||||
// Setup admiralty for each node
|
||||
for _, peer := range b.RemotePeers {
|
||||
fmt.Println("DEV :: Launching Admiralty Setup for ", peer)
|
||||
setter.InitializeAdmiralty(conf.GetConfig().PeerID,peer)
|
||||
}
|
||||
|
||||
// Update the name of the admiralty node to use
|
||||
for _, template := range b.Workflow.Spec.Templates {
|
||||
if len(template.Metadata.Annotations) > 0 {
|
||||
if resp, ok := template.Metadata.Annotations["multicluster.admiralty.io/clustername"]; ok {
|
||||
fmt.Println(resp)
|
||||
template.Metadata.Annotations["multicluster.admiralty.io/clustername"] = "target-" + conf.GetConfig().ExecutionID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"oc-monitord/models"
|
||||
"strings"
|
||||
|
||||
"cloud.o-forge.io/core/oc-lib/models/resources/processing"
|
||||
"cloud.o-forge.io/core/oc-lib/models/resources"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func (b *ArgoBuilder) CreateService(id string, processing *processing.ProcessingResource) {
|
||||
func (b *ArgoBuilder) CreateService(id string, processing *resources.ProcessingResource) {
|
||||
new_service := models.Service{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
@@ -28,16 +28,19 @@ func (b *ArgoBuilder) CreateService(id string, processing *processing.Processing
|
||||
b.Services = append(b.Services, &new_service)
|
||||
}
|
||||
|
||||
func (b *ArgoBuilder) completeServicePorts(service *models.Service, id string, processing *processing.ProcessingResource) {
|
||||
for _, execute := range processing.Expose {
|
||||
if execute.PAT != 0 {
|
||||
new_port_translation := models.ServicePort{
|
||||
Name: strings.ToLower(processing.Name) + id,
|
||||
Port: execute.Port,
|
||||
TargetPort: execute.PAT,
|
||||
Protocol: "TCP",
|
||||
func (b *ArgoBuilder) completeServicePorts(service *models.Service, id string, processing *resources.ProcessingResource) {
|
||||
instance := processing.GetSelectedInstance()
|
||||
if instance != nil && instance.(*resources.ProcessingInstance).Access != nil && instance.(*resources.ProcessingInstance).Access.Container != nil {
|
||||
for _, execute := range instance.(*resources.ProcessingInstance).Access.Container.Exposes {
|
||||
if execute.PAT != 0 {
|
||||
new_port_translation := models.ServicePort{
|
||||
Name: strings.ToLower(processing.Name) + id,
|
||||
Port: execute.Port,
|
||||
TargetPort: execute.PAT,
|
||||
Protocol: "TCP",
|
||||
}
|
||||
service.Spec.Ports = append(service.Spec.Ports, new_port_translation)
|
||||
}
|
||||
service.Spec.Ports = append(service.Spec.Ports, new_port_translation)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +49,6 @@ func (b *ArgoBuilder) addServiceToArgo() error {
|
||||
for _, service := range b.Services {
|
||||
service_manifest, err := yaml.Marshal(service)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not marshal service manifest : " + err.Error())
|
||||
return err
|
||||
}
|
||||
service_template := models.Template{Name: "workflow-service-pod",
|
||||
|
||||
@@ -13,20 +13,21 @@ type WorflowDB struct {
|
||||
}
|
||||
|
||||
// Create the obj!ects from the mxgraphxml stored in the workflow given as a parameter
|
||||
func (w *WorflowDB) LoadFrom(workflow_id string) error {
|
||||
func (w *WorflowDB) LoadFrom(workflow_id string, peerID string) error {
|
||||
fmt.Println("Loading workflow from " + workflow_id)
|
||||
var err error
|
||||
if w.Workflow, err = w.getWorkflow(workflow_id); err != nil {
|
||||
if w.Workflow, err = w.getWorkflow(workflow_id, peerID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use oclib to retrieve the graph contained in the workflow referenced
|
||||
func (w *WorflowDB) getWorkflow(workflow_id string) (workflow *workflow.Workflow, err error) {
|
||||
func (w *WorflowDB) getWorkflow(workflow_id string, peerID string) (workflow *workflow.Workflow, err error) {
|
||||
logger := oclib.GetLogger()
|
||||
|
||||
lib_data := oclib.LoadOne(oclib.LibDataEnum(oclib.WORKFLOW), workflow_id)
|
||||
fmt.Println(lib_data.Code, lib_data.Err)
|
||||
lib_data := oclib.NewRequest(oclib.LibDataEnum(oclib.WORKFLOW), "", peerID, []string{}, nil).LoadOne(workflow_id)
|
||||
fmt.Println("ERR", lib_data.Code, lib_data.Err)
|
||||
if lib_data.Code != 200 {
|
||||
logger.Error().Msg("Error loading the graph")
|
||||
return workflow, errors.New(lib_data.Err)
|
||||
@@ -40,20 +41,20 @@ func (w *WorflowDB) getWorkflow(workflow_id string) (workflow *workflow.Workflow
|
||||
return new_wf, nil
|
||||
}
|
||||
|
||||
func (w *WorflowDB) ExportToArgo(timeout int) (string, int, error) {
|
||||
func (w *WorflowDB) ExportToArgo(namespace string, timeout int) (*ArgoBuilder, int, error) {
|
||||
logger := oclib.GetLogger()
|
||||
|
||||
fmt.Println("Exporting to Argo", w.Workflow)
|
||||
if len(w.Workflow.Name) == 0 || w.Workflow.Graph == nil {
|
||||
return "", 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")
|
||||
}
|
||||
|
||||
argo_builder := ArgoBuilder{OriginWorkflow: w.Workflow, Timeout: timeout}
|
||||
filename, stepMax, _, _, err := argo_builder.CreateDAG(true)
|
||||
argoBuilder := ArgoBuilder{OriginWorkflow: w.Workflow, Timeout: timeout}
|
||||
stepMax, _, _, err := argoBuilder.CreateDAG(namespace, true)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not create the argo file for " + w.Workflow.Name)
|
||||
return "", 0, err
|
||||
return nil, 0, err
|
||||
}
|
||||
return filename, stepMax, nil
|
||||
return &argoBuilder, stepMax, nil
|
||||
}
|
||||
|
||||
// TODO implement this function
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetGraph(t *testing.T){
|
||||
func TestGetGraph(t *testing.T) {
|
||||
w := WorflowDB{}
|
||||
w.LoadFrom("test-log")
|
||||
w.LoadFrom("test-log", "")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user