diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1ff2fb15..10772d66 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,6 +13,16 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: true + - name: Unit Tests + uses: dagger/dagger-for-github@v7 + with: + workdir: ci + verb: call + args: -s --name unit-tests unit-test + cloud-token: ${{ secrets.DAGGER_CLOUD_TOKEN }} + version: "0.19.2" - name: Integration Test uses: dagger/dagger-for-github@v7 with: @@ -20,12 +30,12 @@ jobs: verb: call args: -s --name slurm-test build-images new-interlink test stdout cloud-token: ${{ secrets.DAGGER_CLOUD_TOKEN }} - version: "0.18.9" + version: "0.19.2" - name: Integration Test mTLS uses: dagger/dagger-for-github@v7 with: workdir: ci verb: call - args: -s --name slurm-test-mtls build-images new-interlink-mtls test stdout + args: -s --name slurm-test-mtls build-images new-interlink-mtls test stdout cloud-token: ${{ secrets.DAGGER_CLOUD_TOKEN }} - version: "0.18.9" + version: "0.19.2" diff --git a/.gitignore b/.gitignore index 2152b023..c2bbeb26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ interlink-install +QWEN.md +coverage.out vendor dist/* docs/yarn.lock diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..db00da61 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "helm"] + path = helm + url = https://github.com/interlink-hq/interlink-helm-chart +[submodule "plugins/slurm"] + path = plugins/slurm + url = https://github.com/interlink-hq/interlink-slurm-plugin +[submodule "test/vk-test-set"] + path = test/vk-test-set + url = https://github.com/interlink-hq/vk-test-set diff --git a/Makefile b/Makefile index eac91608..2a8d7235 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,9 @@ openapi: clean: rm -rf ./bin +unit-test: + go test -v -race -coverprofile=coverage.out -covermode=atomic ./pkg/... + test: dagger call -m ./ci \ --name my-tests \ diff --git a/ci/.gitignore b/ci/.gitignore index 1d703bb5..0ccced65 100644 --- a/ci/.gitignore +++ b/ci/.gitignore @@ -3,3 +3,4 @@ /internal/querybuilder /internal/telemetry kubeconfig.yaml +/.env diff --git a/ci/dagger.json b/ci/dagger.json index 8731bba5..a35ba4d3 100644 --- a/ci/dagger.json +++ b/ci/dagger.json @@ -1,6 +1,6 @@ { "name": "interlink", - "engineVersion": "v0.18.9", + "engineVersion": "v0.19.2", "sdk": { "source": "go" }, @@ -8,7 +8,7 @@ { "name": "k3s", "source": "github.com/marcosnils/daggerverse/k3s@k3s/v0.1.10", - "pin": "8b07ea65d6d60b79d8e28db965f0a3bb2fa58541" + "pin": "28eea1fcf3b6ecb38a628186107760acd717442f" } ] } diff --git a/ci/go.mod b/ci/go.mod index aa1c7b4a..1af6432a 100644 --- a/ci/go.mod +++ b/ci/go.mod @@ -1,53 +1,51 @@ module dagger/interlink -go 1.23.0 - -toolchain go1.23.6 +go 1.24.0 require ( - github.com/99designs/gqlgen v0.17.73 + github.com/99designs/gqlgen v0.17.80 github.com/Khan/genqlient v0.8.1 - github.com/vektah/gqlparser/v2 v2.5.27 - go.opentelemetry.io/otel v1.34.0 - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 - go.opentelemetry.io/otel/log v0.8.0 - go.opentelemetry.io/otel/sdk v1.34.0 - go.opentelemetry.io/otel/sdk/log v0.8.0 - go.opentelemetry.io/otel/trace v1.34.0 - go.opentelemetry.io/proto/otlp v1.3.1 - golang.org/x/sync v0.14.0 - google.golang.org/grpc v1.72.1 + github.com/vektah/gqlparser/v2 v2.5.30 + go.opentelemetry.io/otel v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 + go.opentelemetry.io/otel/log v0.14.0 + go.opentelemetry.io/otel/sdk v1.38.0 + go.opentelemetry.io/otel/sdk/log v0.14.0 + go.opentelemetry.io/otel/trace v1.38.0 + go.opentelemetry.io/proto/otlp v1.8.0 + golang.org/x/sync v0.17.0 + google.golang.org/grpc v1.75.1 ) require ( - github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sosodev/duration v1.3.1 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 - go.opentelemetry.io/otel/sdk/metric v1.34.0 - golang.org/x/net v0.40.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect - google.golang.org/protobuf v1.36.6 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 + go.opentelemetry.io/otel/sdk/metric v1.38.0 + golang.org/x/net v0.44.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/protobuf v1.36.9 // indirect ) -replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 -replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 -replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.8.0 +replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.14.0 -replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.8.0 +replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.14.0 diff --git a/ci/go.sum b/ci/go.sum index 88f69b87..d49b1da2 100644 --- a/ci/go.sum +++ b/ci/go.sum @@ -1,27 +1,27 @@ -github.com/99designs/gqlgen v0.17.73 h1:A3Ki+rHWqKbAOlg5fxiZBnz6OjW3nwupDHEG15gEsrg= -github.com/99designs/gqlgen v0.17.73/go.mod h1:2RyGWjy2k7W9jxrs8MOQthXGkD3L3oGr0jXW3Pu8lGg= +github.com/99designs/gqlgen v0.17.80 h1:S64VF9SK+q3JjQbilgdrM0o4iFQgB54mVQ3QvXEO4Ek= +github.com/99designs/gqlgen v0.17.80/go.mod h1:vgNcZlLwemsUhYim4dC1pvFP5FX0pr2Y+uYUoHFb1ig= github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs= github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= 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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -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/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -33,60 +33,64 @@ github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -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/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s= -github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE= +github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI= -go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= -go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= -go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs= -go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo= -go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= -go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 h1:OMqPldHt79PqWKOMYIAQs3CxAi7RLgPxwfFSwr4ZxtM= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0/go.mod h1:1biG4qiqTxKiUCtoWDPpL3fB3KxVwCiGw81j3nKMuHE= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 h1:QQqYw3lkrzwVsoEX0w//EhH/TCnpRdEenKBOOEIMjWc= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0/go.mod h1:gSVQcr17jk2ig4jqJ2DX30IdWH251JcNAecvrqTxH1s= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= +go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= +go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg= +go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM= +go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM= +go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE= +go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0= -google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/ci/main.go b/ci/main.go index d2d1515d..240a5948 100644 --- a/ci/main.go +++ b/ci/main.go @@ -180,6 +180,7 @@ func New(name string, InterlinkRef: InterlinkRef, InterlinkContainer: dag.Container(), PluginRef: pluginRef, + PluginContainer: dag.Container(), } } @@ -206,6 +207,9 @@ func (m *Interlink) NewInterlink( // +optional // +defaultPath="./manifests/plugin-config.yaml" pluginConfig *dagger.File, + // +optional + // +defaultPath="../helm" + helmChart *dagger.Directory, ) (*Interlink, error) { if localRegistry != nil { m.Registry = localRegistry @@ -218,16 +222,30 @@ func (m *Interlink) NewInterlink( // docker run -p 4000:4000 -v ./manifests/plugin-config.yaml:/etc/interlink/InterLinkConfig.yaml -e SHARED_FS=true -e SLURMCONFIGPATH=/etc/interlink/InterLinkConfig.yaml ghcr.io/interlink-hq/interlink-sidecar-slurm/interlink-sidecar-slurm:0.4.0 var err error if pluginEndpoint == nil { - m.PluginContainer = dag.Container().From(m.PluginRef). - WithFile("/etc/interlink/InterLinkConfig.yaml", pluginConfig). - WithEnvVariable("BUST", time.Now().String()). - WithEnvVariable("SLURMCONFIGPATH", "/etc/interlink/InterLinkConfig.yaml"). - WithEnvVariable("SHARED_FS", "true"). - WithExposedPort(4000) - - pluginEndpoint, err = m.PluginContainer.AsService(dagger.ContainerAsServiceOpts{UseEntrypoint: true, InsecureRootCapabilities: true}).Start(ctx) - if err != nil { - return nil, err + if m.PluginContainer == nil { + m.PluginContainer = dag.Container().From(m.PluginRef). + WithFile("/etc/interlink/InterLinkConfig.yaml", pluginConfig). + WithEnvVariable("BUST", time.Now().String()). + WithEnvVariable("SLURMCONFIGPATH", "/etc/interlink/InterLinkConfig.yaml"). + WithEnvVariable("SHARED_FS", "true"). + WithExposedPort(4000) + + pluginEndpoint, err = m.PluginContainer.AsService(dagger.ContainerAsServiceOpts{UseEntrypoint: true, InsecureRootCapabilities: true}).Start(ctx) + if err != nil { + return nil, err + } + } else { + m.PluginContainer = m.PluginContainer. + WithFile("/etc/interlink/InterLinkConfig.yaml", pluginConfig). + WithEnvVariable("BUST", time.Now().String()). + WithEnvVariable("SLURMCONFIGPATH", "/etc/interlink/InterLinkConfig.yaml"). + WithEnvVariable("SHARED_FS", "true"). + WithExposedPort(4000) + + pluginEndpoint, err = m.PluginContainer.AsService(dagger.ContainerAsServiceOpts{UseEntrypoint: true, InsecureRootCapabilities: true}).Start(ctx) + if err != nil { + return nil, err + } } } @@ -331,6 +349,7 @@ EOF`}). dag.Container().From("alpine/helm:3.16.1"). WithMountedFile("/.kube/config", m.KubeConfig). + WithDirectory("/helm", helmChart). WithEnvVariable("BUST", time.Now().String()). WithEnvVariable("KUBECONFIG", "/.kube/config"). WithNewFile("/manifests/vk_helm_chart.yaml", bufferVK.String(), dagger.ContainerWithNewFileOpts{ @@ -342,8 +361,7 @@ EOF`}). "--create-namespace", "-n", "interlink", "virtual-node", - "oci://ghcr.io/interlink-hq/interlink-helm-chart/interlink", - "--version", "0.5.3-pre3", + "/helm/interlink", "--values", "/manifests/vk_helm_chart.yaml", }).Stdout(ctx) @@ -366,6 +384,9 @@ func (m *Interlink) NewInterlinkMTLS( interlinkEndpoint *dagger.Service, // +optional pluginEndpoint *dagger.Service, + // +optional + // +defaultPath="../helm" + helmChart *dagger.Directory, ) (*Interlink, error) { if localRegistry != nil { m.Registry = localRegistry @@ -430,16 +451,30 @@ BashPath: /bin/bash var err error // Setup plugin with standard config if pluginEndpoint == nil { - m.PluginContainer = dag.Container().From(m.PluginRef). - WithFile("/etc/interlink/InterLinkConfig.yaml", pluginConfigFile.File("/etc/interlink/InterLinkConfig.yaml")). - WithEnvVariable("BUST", time.Now().String()). - WithEnvVariable("SLURMCONFIGPATH", "/etc/interlink/InterLinkConfig.yaml"). - WithEnvVariable("SHARED_FS", "true"). - WithExposedPort(4000) - - pluginEndpoint, err = m.PluginContainer.AsService(dagger.ContainerAsServiceOpts{Args: []string{}, UseEntrypoint: true, InsecureRootCapabilities: true}).Start(ctx) - if err != nil { - return nil, err + if m.PluginContainer == nil { + m.PluginContainer = dag.Container().From(m.PluginRef). + WithFile("/etc/interlink/InterLinkConfig.yaml", pluginConfigFile.File("/etc/interlink/InterLinkConfig.yaml")). + WithEnvVariable("BUST", time.Now().String()). + WithEnvVariable("SLURMCONFIGPATH", "/etc/interlink/InterLinkConfig.yaml"). + WithEnvVariable("SHARED_FS", "true"). + WithExposedPort(4000) + + pluginEndpoint, err = m.PluginContainer.AsService(dagger.ContainerAsServiceOpts{UseEntrypoint: true, InsecureRootCapabilities: true}).Start(ctx) + if err != nil { + return nil, err + } + } else { + m.PluginContainer = m.PluginContainer. + WithFile("/etc/interlink/InterLinkConfig.yaml", pluginConfigFile.File("/etc/interlink/InterLinkConfig.yaml")). + WithEnvVariable("BUST", time.Now().String()). + WithEnvVariable("SLURMCONFIGPATH", "/etc/interlink/InterLinkConfig.yaml"). + WithEnvVariable("SHARED_FS", "true"). + WithExposedPort(4000) + + pluginEndpoint, err = m.PluginContainer.AsService(dagger.ContainerAsServiceOpts{UseEntrypoint: true, InsecureRootCapabilities: true}).Start(ctx) + if err != nil { + return nil, err + } } } @@ -571,6 +606,7 @@ EOF`}). // Deploy with mTLS certificates mounted dag.Container().From("alpine/helm:3.16.1"). WithMountedFile("/.kube/config", m.KubeConfig). + WithDirectory("/helm", helmChart). WithEnvVariable("BUST", time.Now().String()). WithEnvVariable("KUBECONFIG", "/.kube/config"). WithNewFile("/manifests/vk_helm_chart_mtls.yaml", bufferVK.String(), dagger.ContainerWithNewFileOpts{ @@ -582,8 +618,7 @@ EOF`}). "--create-namespace", "-n", "interlink", "virtual-node-mtls", - "oci://ghcr.io/interlink-hq/interlink-helm-chart/interlink", - "--version", "0.5.3-pre3", + "/helm/interlink", "--values", "/manifests/vk_helm_chart_mtls.yaml", }).Stdout(ctx) @@ -620,6 +655,9 @@ func (m *Interlink) BuildImages( // +optional // +defaultPath="../" sourceFolder *dagger.Directory, + // +optional + // +defaultPath="../plugins/slurm" + pluginSource *dagger.Directory, ) (*Interlink, error) { // TODO: get tag m.Registry = dag.Container().From("registry"). @@ -690,6 +728,25 @@ func (m *Interlink) BuildImages( if err != nil { return nil, err } + + // Build plugin from local submodule using Dockerfile + m.PluginRef = pluginRef + + m.PluginContainer = pluginSource. + DockerBuild(dagger.DirectoryDockerBuildOpts{ + Dockerfile: "docker/Dockerfile", + }) + + _, err = dag.Container().From("quay.io/skopeo/stable"). + WithEnvVariable("BUST", time.Now().String()). + WithServiceBinding("registry", m.Registry). + WithMountedFile("image.tar", m.PluginContainer.AsTarball()). + WithExec([]string{"copy", "--dest-tls-verify=false", "docker-archive:image.tar", "docker://" + m.PluginRef}, dagger.ContainerWithExecOpts{UseEntrypoint: true}). + Sync(ctx) + if err != nil { + return nil, err + } + return m, nil } @@ -706,6 +763,9 @@ func (m *Interlink) Run( // +optional // +defaultPath="./manifests" manifests *dagger.Directory, + // +optional + // +defaultPath="../test/vk-test-set" + testSet *dagger.Directory, ) (*dagger.Container, error) { return dag.Container().From("bitnamilegacy/kubectl:1.33-debian-12"). WithUser("root"). @@ -719,11 +779,12 @@ func (m *Interlink) Run( WithExec([]string{"chown", "1001:0", "/.kube/config"}). WithUser("1001"). WithDirectory("/manifests", manifests). + WithDirectory("/opt/user/vk-test-set", testSet, dagger.ContainerWithDirectoryOpts{Owner: "1001:0"}). WithEntrypoint([]string{"kubectl"}). - WithWorkdir("/opt/user"). - WithExec([]string{"bash", "-c", "git clone https://github.com/interlink-hq/vk-test-set.git"}). WithExec([]string{"bash", "-c", "cp /manifests/vktest_config.yaml /opt/user/vk-test-set/vktest_config.yaml"}). WithWorkdir("/opt/user/vk-test-set"). + // Automate CSR approval for testing - required for mTLS functionality and log access + WithExec([]string{"bash", "-c", "kubectl get csr -o name | xargs -r kubectl certificate approve"}). WithExec([]string{"bash", "-c", "python3 -m venv .venv && source .venv/bin/activate && pip3 install -e ./ "}), nil } @@ -734,13 +795,34 @@ func (m *Interlink) Lint( ) *dagger.Container { lintCache := dag.CacheVolume(m.Name + "_lint") - return dag.Container().From("golangci/golangci-lint:v2.1.1"). + return dag.Container().From("golangci/golangci-lint:v2.5.0"). WithMountedDirectory("/app", sourceFolder). WithMountedCache("/root/.cache", lintCache). WithWorkdir("/app"). WithExec([]string{"golangci-lint", "run", "-v", "--timeout=30m"}, dagger.ContainerWithExecOpts{UseEntrypoint: true}) } +// UnitTest runs the unit tests for the project +func (m *Interlink) UnitTest( + ctx context.Context, + // +optional + // +defaultPath="../" + sourceFolder *dagger.Directory, +) (string, error) { + buildCache := dag.CacheVolume(m.Name + "_go-build") + modCache := dag.CacheVolume(m.Name + "_go-mod") + + return dag.Container().From("golang:1.24"). + WithMountedDirectory("/src", sourceFolder). + WithWorkdir("/src"). + WithMountedCache("/go/pkg/mod", modCache). + WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). + WithMountedCache("/go/build-cache", buildCache). + WithEnvVariable("GOCACHE", "/go/build-cache"). + WithExec([]string{"go", "test", "-v", "-race", "-coverprofile=coverage.out", "-covermode=atomic", "./pkg/..."}). + Stdout(ctx) +} + // Wait for cluster to be ready, setup the test container, run all tests func (m *Interlink) Test( ctx context.Context, @@ -752,14 +834,24 @@ func (m *Interlink) Test( // +optional // +defaultPath="../" sourceFolder *dagger.Directory, + // +optional + // +defaultPath="../test/vk-test-set" + testSet *dagger.Directory, ) (*dagger.Container, error) { + // Run unit tests first + unitTestOutput, err := m.UnitTest(ctx, sourceFolder) + if err != nil { + return nil, err + } + log.Printf("Unit test output: %s", unitTestOutput) + lint, err := m.Lint(sourceFolder).Stdout(ctx) if err != nil { return nil, err } log.Printf("Lint output: %s", lint) - c, err := m.Run(ctx, manifests) + c, err := m.Run(ctx, manifests, testSet) if err != nil { return nil, err } @@ -785,17 +877,29 @@ func (m *Interlink) TestMTLS( // +optional // +defaultPath="../" sourceFolder *dagger.Directory, + // +optional + // +defaultPath="../test/vk-test-set" + testSet *dagger.Directory, ) (*dagger.Container, error) { + // Run unit tests first + unitTestOutput, err := m.UnitTest(ctx, sourceFolder) + if err != nil { + return nil, err + } + log.Printf("Unit test output: %s", unitTestOutput) + lint, err := m.Lint(sourceFolder).Stdout(ctx) if err != nil { return nil, err } log.Printf("Lint output: %s", lint) - c, err := m.Run(ctx, manifests) + c, err := m.Run(ctx, manifests, testSet) if err != nil { return nil, err } + // Automate CSR approval for testing - required for mTLS functionality and log access + c = c.WithExec([]string{"bash", "-c", "kubectl get csr -o name | xargs -r kubectl certificate approve"}) // First run basic tests to ensure setup works result := c.WithExec([]string{"bash", "-c", "source .venv/bin/activate && export KUBECONFIG=/.kube/config && pytest -v -k 'hello'"}). @@ -831,6 +935,8 @@ EOF`}). WithExec([]string{"bash", "-c", "timeout 10s kubectl logs -f mtls-log-test || echo 'Log streaming test completed'"}). // Clean up test pod WithExec([]string{"bash", "-c", "kubectl delete pod mtls-log-test --ignore-not-found"}). + // Automate CSR approval for testing - required for mTLS functionality and log access + WithExec([]string{"bash", "-c", "kubectl get csr -o name | xargs -r kubectl certificate approve"}). // Run the full test suite (excluding resource-intensive tests) WithExec([]string{"bash", "-c", "source .venv/bin/activate && export KUBECONFIG=/.kube/config && pytest -v -k 'not rclone and not limits and not stress'"}) diff --git a/example/interlink-config.yaml b/example/interlink-config.yaml deleted file mode 100644 index 121bb613..00000000 --- a/example/interlink-config.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# apiVersion: v1 -# kind: ConfigMap -# metadata: -# name: "interlink-config" -# namespace: interlink -# data: -# InterLinkConfig.yaml: | - #InterlinkAddress: "unix:///var/run/interlink.socket" -InterlinkAddress: "http://0.0.0.0" -InterlinkPort: "3000" -#sidecarURL: "http://plugin" -SidecarURL: "http://172.17.0.1" -SidecarPort: "4000" -VerboseLogging: true -ErrorsOnlyLogging: false -DataRootFolder: "~/.interlink" diff --git a/example/plugin-config.yaml b/example/plugin-config.yaml deleted file mode 100644 index a5eb0261..00000000 --- a/example/plugin-config.yaml +++ /dev/null @@ -1,21 +0,0 @@ -InterlinkURL: "http://interlink" -InterlinkPort: "3000" -SidecarURL: "http://0.0.0.0" -SidecarPort: "4000" -VerboseLogging: true -ErrorsOnlyLogging: false -# NEEDED PATH FOR GITHUB ACTIONS -#DataRootFolder: "/home/runner/work/interLink/interLink/.interlink/" -# on your host use something like: -DataRootFolder: "/home/ubuntu/.interlink/" -ExportPodData: true -SbatchPath: "/usr/bin/sbatch" -ScancelPath: "/usr/bin/scancel" -SqueuePath: "/usr/bin/squeue" -CommandPrefix: "" -SingularityPrefix: "" -Namespace: "vk" -Tsocks: false -TsocksPath: "$WORK/tsocks-1.8beta5+ds1/libtsocks.so" -TsocksLoginNode: "login01" -BashPath: /bin/bash diff --git a/example/vk-config.yaml b/example/vk-config.yaml deleted file mode 100644 index 7bd05c26..00000000 --- a/example/vk-config.yaml +++ /dev/null @@ -1,16 +0,0 @@ -InterlinkURL: "http://localhost" -InterlinkPort: "3000" -VerboseLogging: true -ErrorsOnlyLogging: false -ServiceAccount: "virtual-kubelet" -Namespace: interlink -VKTokenFile: "" -Resources: - CPU: "100" - Memory: "128Gi" - Pods: "100" -HTTP: - Insecure: true -KubeletHTTP: - Insecure: true - diff --git a/helm b/helm new file mode 160000 index 00000000..9a685958 --- /dev/null +++ b/helm @@ -0,0 +1 @@ +Subproject commit 9a6859581f0c7dd26db8a208b00ae44aed4cb4eb diff --git a/pkg/interlink/api/handler_test.go b/pkg/interlink/api/handler_test.go new file mode 100644 index 00000000..520d0f97 --- /dev/null +++ b/pkg/interlink/api/handler_test.go @@ -0,0 +1,319 @@ +package api + +import ( + "context" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" +) + +func TestGetSessionContext(t *testing.T) { + tests := []struct { + name string + headerValue string + expectGenerate bool + }{ + { + name: "existing session context", + headerValue: "Request-12345", + expectGenerate: false, + }, + { + name: "no session context - should generate", + headerValue: "", + expectGenerate: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/test", nil) + if tt.headerValue != "" { + req.Header.Set("InterLink-Http-Session", tt.headerValue) + } + + got := GetSessionContext(req) + + if tt.expectGenerate { + assert.NotEmpty(t, got) + assert.Contains(t, got, "Request-") + } else { + assert.Equal(t, tt.headerValue, got) + } + }) + } +} + +func TestAddSessionContext(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/test", nil) + sessionID := "Request-test-123" + + AddSessionContext(req, sessionID) + + got := req.Header.Get("InterLink-Http-Session") + assert.Equal(t, sessionID, got) +} + +func TestGetSessionContextMessage(t *testing.T) { + tests := []struct { + name string + sessionContext string + expected string + }{ + { + name: "format session context message", + sessionContext: "Request-12345", + expected: "HTTP InterLink session Request-12345: ", + }, + { + name: "empty session context", + sessionContext: "", + expected: "HTTP InterLink session : ", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := GetSessionContextMessage(tt.sessionContext) + assert.Equal(t, tt.expected, got) + }) + } +} + +func setupTestTracer() (*trace.TracerProvider, func()) { + exporter := tracetest.NewInMemoryExporter() + tp := trace.NewTracerProvider( + trace.WithSyncer(exporter), + ) + otel.SetTracerProvider(tp) + + cleanup := func() { + if err := tp.Shutdown(context.Background()); err != nil { + panic(err) + } + } + + return tp, cleanup +} + +func TestReqWithError_HeadersSet(t *testing.T) { + tp, cleanup := setupTestTracer() + defer cleanup() + + tracer := tp.Tracer("test") + ctx, span := tracer.Start(context.Background(), "test-span") + defer span.End() + + // Create a test server that echoes back request headers + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Verify headers are set correctly + assert.Equal(t, "application/json", r.Header.Get("Content-Type")) + assert.NotEmpty(t, r.Header.Get("InterLink-Http-Session")) + + w.WriteHeader(http.StatusOK) + if _, err := io.WriteString(w, `{"status":"ok"}`); err != nil { + panic(err) + } + })) + defer testServer.Close() + + // Create request to test server + req, err := http.NewRequest(http.MethodGet, testServer.URL, nil) + require.NoError(t, err) + + // Create response recorder + w := httptest.NewRecorder() + + // Create HTTP client + client := testServer.Client() + + // Call ReqWithError + sessionContext := "Request-test-123" + startTime := time.Now().UnixMicro() + _, err = ReqWithError( + ctx, + req, + w, + startTime, + span, + false, + true, + sessionContext, + client, + ) + + assert.NoError(t, err) +} + +func TestReqWithError_ErrorHandling(t *testing.T) { + tp, cleanup := setupTestTracer() + defer cleanup() + + tests := []struct { + name string + serverStatus int + serverResponse string + expectError bool + }{ + { + name: "successful request", + serverStatus: http.StatusOK, + serverResponse: `{"status":"ok"}`, + expectError: false, + }, + { + name: "server error", + serverStatus: http.StatusInternalServerError, + serverResponse: "internal server error", + expectError: true, + }, + { + name: "bad request", + serverStatus: http.StatusBadRequest, + serverResponse: "bad request", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tracer := tp.Tracer("test") + ctx, span := tracer.Start(context.Background(), "test-span") + defer span.End() + + // Create test server with specific response + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(tt.serverStatus) + if _, err := io.WriteString(w, tt.serverResponse); err != nil { + panic(err) + } + })) + defer testServer.Close() + + req, err := http.NewRequest(http.MethodGet, testServer.URL, nil) + require.NoError(t, err) + + w := httptest.NewRecorder() + client := testServer.Client() + startTime := time.Now().UnixMicro() + + _, err = ReqWithError( + ctx, + req, + w, + startTime, + span, + true, + true, + "Request-test", + client, + ) + + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestReqWithError_ResponseModes(t *testing.T) { + tp, cleanup := setupTestTracer() + defer cleanup() + + testData := `{"test":"data","value":123}` + + tests := []struct { + name string + respondWithValues bool + respondWithReturn bool + expectReturnData bool + expectWriteData bool + }{ + { + name: "return and write data", + respondWithValues: true, + respondWithReturn: true, + expectReturnData: true, + expectWriteData: true, + }, + { + name: "only return data", + respondWithValues: false, + respondWithReturn: true, + expectReturnData: true, + expectWriteData: false, + }, + { + name: "only write data (streaming)", + respondWithValues: true, + respondWithReturn: false, + expectReturnData: false, + expectWriteData: true, + }, + { + name: "neither return nor write", + respondWithValues: false, + respondWithReturn: false, + expectReturnData: false, + expectWriteData: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tracer := tp.Tracer("test") + ctx, span := tracer.Start(context.Background(), "test-span") + defer span.End() + + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + if _, err := io.WriteString(w, testData); err != nil { + panic(err) + } + })) + defer testServer.Close() + + req, err := http.NewRequest(http.MethodGet, testServer.URL, nil) + require.NoError(t, err) + + w := httptest.NewRecorder() + client := testServer.Client() + startTime := time.Now().UnixMicro() + + returnedData, err := ReqWithError( + ctx, + req, + w, + startTime, + span, + tt.respondWithValues, + tt.respondWithReturn, + "Request-test", + client, + ) + + require.NoError(t, err) + + if tt.expectReturnData { + assert.NotNil(t, returnedData) + assert.Contains(t, string(returnedData), "test") + } else { + assert.Nil(t, returnedData) + } + + if tt.expectWriteData { + assert.NotEmpty(t, w.Body.String()) + } + }) + } +} diff --git a/pkg/interlink/config_test.go b/pkg/interlink/config_test.go new file mode 100644 index 00000000..da459a9f --- /dev/null +++ b/pkg/interlink/config_test.go @@ -0,0 +1,306 @@ +package interlink + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" +) + +func TestConfig_LoadFromYAML(t *testing.T) { + tests := []struct { + name string + yamlContent string + want Config + wantErr bool + }{ + { + name: "basic config without TLS", + yamlContent: ` +InterlinkAddress: "http://0.0.0.0" +InterlinkPort: "3000" +SidecarURL: "http://localhost" +SidecarPort: "4000" +VerboseLogging: true +ErrorsOnlyLogging: false +DataRootFolder: "/tmp/interlink" +`, + want: Config{ + InterlinkAddress: "http://0.0.0.0", + Interlinkport: "3000", + Sidecarurl: "http://localhost", + Sidecarport: "4000", + VerboseLogging: true, + ErrorsOnlyLogging: false, + DataRootFolder: "/tmp/interlink", + }, + wantErr: false, + }, + { + name: "config with TLS enabled", + yamlContent: ` +InterlinkAddress: "https://0.0.0.0" +InterlinkPort: "3000" +SidecarURL: "http://localhost" +SidecarPort: "4000" +VerboseLogging: false +ErrorsOnlyLogging: false +DataRootFolder: "/tmp/interlink" +TLS: + Enabled: true + CertFile: "/certs/server.crt" + KeyFile: "/certs/server.key" + CACertFile: "/certs/ca.crt" +`, + want: Config{ + InterlinkAddress: "https://0.0.0.0", + Interlinkport: "3000", + Sidecarurl: "http://localhost", + Sidecarport: "4000", + VerboseLogging: false, + ErrorsOnlyLogging: false, + DataRootFolder: "/tmp/interlink", + TLS: TLSConfig{ + Enabled: true, + CertFile: "/certs/server.crt", + KeyFile: "/certs/server.key", + CACertFile: "/certs/ca.crt", + }, + }, + wantErr: false, + }, + { + name: "config with job script build config", + yamlContent: ` +InterlinkAddress: "http://0.0.0.0" +InterlinkPort: "3000" +SidecarURL: "http://localhost" +SidecarPort: "4000" +VerboseLogging: false +ErrorsOnlyLogging: false +DataRootFolder: "/tmp/interlink" +JobScriptBuildConfig: + singularity_hub: + server: "https://hub.example.com" + master_token: "test-token" + cache_validity_seconds: 3600 + apptainer_options: + executable: "/usr/bin/apptainer" + fakeroot: true + containall: true + nvidia_support: true + volumes_options: + scratch_area: "/scratch" + apptainer_cachedir: "/cache" + image_dir: "/images" +`, + want: Config{ + InterlinkAddress: "http://0.0.0.0", + Interlinkport: "3000", + Sidecarurl: "http://localhost", + Sidecarport: "4000", + VerboseLogging: false, + ErrorsOnlyLogging: false, + DataRootFolder: "/tmp/interlink", + JobScriptBuildConfig: &ScriptBuildConfig{ + SingularityHub: SingularityHubConfig{ + Server: "https://hub.example.com", + MasterToken: "test-token", + CacheValiditySeconds: 3600, + }, + ApptainerOptions: ApptainerOptions{ + Executable: "/usr/bin/apptainer", + Fakeroot: true, + ContainAll: true, + NvidiaSupport: true, + }, + VolumesOptions: VolumesOptions{ + ScratchArea: "/scratch", + ApptainerCacheDir: "/cache", + ImageDir: "/images", + }, + }, + }, + wantErr: false, + }, + { + name: "invalid YAML", + yamlContent: `invalid: yaml: content: [[[`, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create temporary config file + tmpDir := t.TempDir() + configPath := filepath.Join(tmpDir, "config.yaml") + err := os.WriteFile(configPath, []byte(tt.yamlContent), 0600) + require.NoError(t, err) + + // Read and parse config + data, err := os.ReadFile(configPath) + require.NoError(t, err) + + var got Config + err = yaml.Unmarshal(data, &got) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + assert.Equal(t, tt.want.InterlinkAddress, got.InterlinkAddress) + assert.Equal(t, tt.want.Interlinkport, got.Interlinkport) + assert.Equal(t, tt.want.Sidecarurl, got.Sidecarurl) + assert.Equal(t, tt.want.Sidecarport, got.Sidecarport) + assert.Equal(t, tt.want.VerboseLogging, got.VerboseLogging) + assert.Equal(t, tt.want.ErrorsOnlyLogging, got.ErrorsOnlyLogging) + assert.Equal(t, tt.want.DataRootFolder, got.DataRootFolder) + assert.Equal(t, tt.want.TLS, got.TLS) + + if tt.want.JobScriptBuildConfig != nil { + require.NotNil(t, got.JobScriptBuildConfig) + assert.Equal(t, tt.want.JobScriptBuildConfig.SingularityHub, got.JobScriptBuildConfig.SingularityHub) + assert.Equal(t, tt.want.JobScriptBuildConfig.ApptainerOptions, got.JobScriptBuildConfig.ApptainerOptions) + assert.Equal(t, tt.want.JobScriptBuildConfig.VolumesOptions, got.JobScriptBuildConfig.VolumesOptions) + } + }) + } +} + +func TestConfig_EnvironmentOverrides(t *testing.T) { + tests := []struct { + name string + envVars map[string]string + initial Config + expected Config + }{ + { + name: "INTERLINKURL override", + envVars: map[string]string{ + "INTERLINKURL": "http://override:9000", + }, + initial: Config{ + InterlinkAddress: "http://localhost:3000", + }, + expected: Config{ + InterlinkAddress: "http://override:9000", + }, + }, + { + name: "SIDECARURL override", + envVars: map[string]string{ + "SIDECARURL": "http://sidecar-override:5000", + }, + initial: Config{ + Sidecarurl: "http://localhost:4000", + }, + expected: Config{ + Sidecarurl: "http://sidecar-override:5000", + }, + }, + { + name: "multiple overrides", + envVars: map[string]string{ + "INTERLINKURL": "http://new-interlink:8080", + "SIDECARURL": "http://new-sidecar:8081", + "INTERLINKPORT": "9090", + "SIDECARPORT": "9091", + }, + initial: Config{ + InterlinkAddress: "http://old:3000", + Interlinkport: "3000", + Sidecarurl: "http://old:4000", + Sidecarport: "4000", + }, + expected: Config{ + InterlinkAddress: "http://new-interlink:8080", + Interlinkport: "9090", + Sidecarurl: "http://new-sidecar:8081", + Sidecarport: "9091", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set environment variables + for k, v := range tt.envVars { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + + // Apply environment overrides + got := tt.initial + if val := os.Getenv("INTERLINKURL"); val != "" { + got.InterlinkAddress = val + } + if val := os.Getenv("SIDECARURL"); val != "" { + got.Sidecarurl = val + } + if val := os.Getenv("INTERLINKPORT"); val != "" { + got.Interlinkport = val + } + if val := os.Getenv("SIDECARPORT"); val != "" { + got.Sidecarport = val + } + + assert.Equal(t, tt.expected.InterlinkAddress, got.InterlinkAddress) + assert.Equal(t, tt.expected.Interlinkport, got.Interlinkport) + assert.Equal(t, tt.expected.Sidecarurl, got.Sidecarurl) + assert.Equal(t, tt.expected.Sidecarport, got.Sidecarport) + }) + } +} + +func TestTLSConfig_Validation(t *testing.T) { + tests := []struct { + name string + tlsConfig TLSConfig + isValid bool + }{ + { + name: "valid TLS config", + tlsConfig: TLSConfig{ + Enabled: true, + CertFile: "/path/to/cert.pem", + KeyFile: "/path/to/key.pem", + }, + isValid: true, + }, + { + name: "valid mTLS config", + tlsConfig: TLSConfig{ + Enabled: true, + CertFile: "/path/to/cert.pem", + KeyFile: "/path/to/key.pem", + CACertFile: "/path/to/ca.pem", + }, + isValid: true, + }, + { + name: "TLS disabled", + tlsConfig: TLSConfig{ + Enabled: false, + }, + isValid: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Basic validation: if TLS is enabled, cert and key must be provided + if tt.tlsConfig.Enabled { + if tt.isValid { + assert.NotEmpty(t, tt.tlsConfig.CertFile, "CertFile should not be empty when TLS is enabled") + assert.NotEmpty(t, tt.tlsConfig.KeyFile, "KeyFile should not be empty when TLS is enabled") + } + } + }) + } +} diff --git a/pkg/interlink/cri/service_test.go b/pkg/interlink/cri/service_test.go new file mode 100644 index 00000000..b862ea7a --- /dev/null +++ b/pkg/interlink/cri/service_test.go @@ -0,0 +1,301 @@ +package cri + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" +) + +func TestNewFakeRuntimeService(t *testing.T) { + service := NewFakeRuntimeService() + + assert.NotNil(t, service) + assert.NotNil(t, service.Called) + assert.NotNil(t, service.Errors) + assert.NotNil(t, service.Containers) + assert.NotNil(t, service.Sandboxes) + assert.Len(t, service.Called, 0) +} + +func TestFakeRuntimeService_Version(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + resp, err := service.Version(ctx, "v1") + require.NoError(t, err) + assert.NotNil(t, resp) + assert.Equal(t, FakeVersion, resp.Version) + assert.Equal(t, FakeRuntimeName, resp.RuntimeName) + assert.Contains(t, service.Called, "Version") +} + +func TestFakeRuntimeService_Status(t *testing.T) { + service := NewFakeRuntimeService() + service.FakeStatus = &runtimeapi.RuntimeStatus{ + Conditions: []*runtimeapi.RuntimeCondition{ + { + Type: runtimeapi.RuntimeReady, + Status: true, + }, + }, + } + ctx := context.Background() + + resp, err := service.Status(ctx, false) + require.NoError(t, err) + assert.NotNil(t, resp) + assert.NotNil(t, resp.Status) + assert.Contains(t, service.Called, "Status") +} + +func TestFakeRuntimeService_RunPodSandbox(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + config := &runtimeapi.PodSandboxConfig{ + Metadata: &runtimeapi.PodSandboxMetadata{ + Name: "test-pod", + Namespace: "default", + Uid: "12345", + }, + } + + sandboxID, err := service.RunPodSandbox(ctx, config, "") + require.NoError(t, err) + assert.NotEmpty(t, sandboxID) + assert.Contains(t, service.Sandboxes, sandboxID) + assert.Contains(t, service.Called, "RunPodSandbox") +} + +func TestFakeRuntimeService_StopPodSandbox(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + // First create a sandbox + config := &runtimeapi.PodSandboxConfig{ + Metadata: &runtimeapi.PodSandboxMetadata{ + Name: "test-pod", + Namespace: "default", + Uid: "12345", + }, + } + sandboxID, err := service.RunPodSandbox(ctx, config, "") + require.NoError(t, err) + + // Stop the sandbox + err = service.StopPodSandbox(ctx, sandboxID) + require.NoError(t, err) + + // Verify sandbox is not ready + sandbox := service.Sandboxes[sandboxID] + assert.Equal(t, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, sandbox.State) + assert.Contains(t, service.Called, "StopPodSandbox") +} + +func TestFakeRuntimeService_RemovePodSandbox(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + // First create a sandbox + config := &runtimeapi.PodSandboxConfig{ + Metadata: &runtimeapi.PodSandboxMetadata{ + Name: "test-pod", + Namespace: "default", + Uid: "12345", + }, + } + sandboxID, err := service.RunPodSandbox(ctx, config, "") + require.NoError(t, err) + + // Remove the sandbox + err = service.RemovePodSandbox(ctx, sandboxID) + require.NoError(t, err) + + // Verify sandbox is removed + _, exists := service.Sandboxes[sandboxID] + assert.False(t, exists) + assert.Contains(t, service.Called, "RemovePodSandbox") +} + +func TestFakeRuntimeService_CreateContainer(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + // First create a sandbox + sandboxConfig := &runtimeapi.PodSandboxConfig{ + Metadata: &runtimeapi.PodSandboxMetadata{ + Name: "test-pod", + Namespace: "default", + Uid: "12345", + }, + } + sandboxID, err := service.RunPodSandbox(ctx, sandboxConfig, "") + require.NoError(t, err) + + // Create a container + containerConfig := &runtimeapi.ContainerConfig{ + Metadata: &runtimeapi.ContainerMetadata{ + Name: "test-container", + }, + Image: &runtimeapi.ImageSpec{ + Image: "nginx:latest", + }, + } + + containerID, err := service.CreateContainer(ctx, sandboxID, containerConfig, sandboxConfig) + require.NoError(t, err) + assert.NotEmpty(t, containerID) + assert.Contains(t, service.Containers, containerID) + assert.Equal(t, runtimeapi.ContainerState_CONTAINER_CREATED, service.Containers[containerID].State) + assert.Contains(t, service.Called, "CreateContainer") +} + +func TestFakeRuntimeService_StartContainer(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + // Create sandbox and container first + sandboxConfig := &runtimeapi.PodSandboxConfig{ + Metadata: &runtimeapi.PodSandboxMetadata{ + Name: "test-pod", + Namespace: "default", + Uid: "12345", + }, + } + sandboxID, err := service.RunPodSandbox(ctx, sandboxConfig, "") + require.NoError(t, err) + + containerConfig := &runtimeapi.ContainerConfig{ + Metadata: &runtimeapi.ContainerMetadata{ + Name: "test-container", + }, + Image: &runtimeapi.ImageSpec{ + Image: "nginx:latest", + }, + } + containerID, err := service.CreateContainer(ctx, sandboxID, containerConfig, sandboxConfig) + require.NoError(t, err) + + // Start the container + err = service.StartContainer(ctx, containerID) + require.NoError(t, err) + + // Verify container is running + assert.Equal(t, runtimeapi.ContainerState_CONTAINER_RUNNING, service.Containers[containerID].State) + assert.NotZero(t, service.Containers[containerID].StartedAt) + assert.Contains(t, service.Called, "StartContainer") +} + +func TestFakeRuntimeService_StopContainer(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + // Create and start a container + sandboxConfig := &runtimeapi.PodSandboxConfig{ + Metadata: &runtimeapi.PodSandboxMetadata{ + Name: "test-pod", + Namespace: "default", + Uid: "12345", + }, + } + sandboxID, err := service.RunPodSandbox(ctx, sandboxConfig, "") + require.NoError(t, err) + + containerConfig := &runtimeapi.ContainerConfig{ + Metadata: &runtimeapi.ContainerMetadata{ + Name: "test-container", + }, + Image: &runtimeapi.ImageSpec{ + Image: "nginx:latest", + }, + } + containerID, err := service.CreateContainer(ctx, sandboxID, containerConfig, sandboxConfig) + require.NoError(t, err) + err = service.StartContainer(ctx, containerID) + require.NoError(t, err) + + // Stop the container + err = service.StopContainer(ctx, containerID, 10) + require.NoError(t, err) + + // Verify container is exited + assert.Equal(t, runtimeapi.ContainerState_CONTAINER_EXITED, service.Containers[containerID].State) + assert.NotZero(t, service.Containers[containerID].FinishedAt) + assert.Contains(t, service.Called, "StopContainer") +} + +func TestFakeRuntimeService_InjectError(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + // Inject an error for Version call + testErr := assert.AnError + service.InjectError("Version", testErr) + + _, err := service.Version(ctx, "v1") + assert.Error(t, err) + assert.Equal(t, testErr, err) +} + +func TestFakeRuntimeService_ListPodSandbox(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + // Create multiple sandboxes + for i := 0; i < 3; i++ { + config := &runtimeapi.PodSandboxConfig{ + Metadata: &runtimeapi.PodSandboxMetadata{ + Name: "test-pod", + Namespace: "default", + Uid: string(rune(i)), + }, + } + _, err := service.RunPodSandbox(ctx, config, "") + require.NoError(t, err) + } + + // List all sandboxes + sandboxes, err := service.ListPodSandbox(ctx, nil) + require.NoError(t, err) + assert.Len(t, sandboxes, 3) + assert.Contains(t, service.Called, "ListPodSandbox") +} + +func TestFakeRuntimeService_ListContainers(t *testing.T) { + service := NewFakeRuntimeService() + ctx := context.Background() + + // Create sandbox and containers + sandboxConfig := &runtimeapi.PodSandboxConfig{ + Metadata: &runtimeapi.PodSandboxMetadata{ + Name: "test-pod", + Namespace: "default", + Uid: "12345", + }, + } + sandboxID, err := service.RunPodSandbox(ctx, sandboxConfig, "") + require.NoError(t, err) + + for i := 0; i < 2; i++ { + containerConfig := &runtimeapi.ContainerConfig{ + Metadata: &runtimeapi.ContainerMetadata{ + Name: "test-container", + Attempt: uint32(i), + }, + Image: &runtimeapi.ImageSpec{ + Image: "nginx:latest", + }, + } + _, err := service.CreateContainer(ctx, sandboxID, containerConfig, sandboxConfig) + require.NoError(t, err) + } + + // List all containers + containers, err := service.ListContainers(ctx, nil) + require.NoError(t, err) + assert.Len(t, containers, 2) + assert.Contains(t, service.Called, "ListContainers") +} diff --git a/pkg/interlink/types_test.go b/pkg/interlink/types_test.go new file mode 100644 index 00000000..1d6494a2 --- /dev/null +++ b/pkg/interlink/types_test.go @@ -0,0 +1,292 @@ +package interlink + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestPodCreateRequests_JSONSerialization(t *testing.T) { + pod := v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod", + Namespace: "default", + UID: "12345-67890", + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "test-container", + Image: "nginx:latest", + }, + }, + }, + } + + configMap := v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-config", + Namespace: "default", + }, + Data: map[string]string{ + "key": "value", + }, + } + + secret := v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "password": []byte("secret-value"), + }, + } + + request := PodCreateRequests{ + Pod: pod, + ConfigMaps: []v1.ConfigMap{configMap}, + Secrets: []v1.Secret{secret}, + ProjectedVolumeMaps: []v1.ConfigMap{}, + JobScriptBuilderURL: "http://builder.example.com", + } + + // Serialize to JSON + data, err := json.Marshal(request) + require.NoError(t, err) + assert.NotEmpty(t, data) + + // Deserialize from JSON + var decoded PodCreateRequests + err = json.Unmarshal(data, &decoded) + require.NoError(t, err) + + assert.Equal(t, request.Pod.Name, decoded.Pod.Name) + assert.Equal(t, request.Pod.Namespace, decoded.Pod.Namespace) + assert.Len(t, decoded.ConfigMaps, 1) + assert.Equal(t, "test-config", decoded.ConfigMaps[0].Name) + assert.Len(t, decoded.Secrets, 1) + assert.Equal(t, "test-secret", decoded.Secrets[0].Name) + assert.Equal(t, request.JobScriptBuilderURL, decoded.JobScriptBuilderURL) +} + +func TestPodStatus_JSONSerialization(t *testing.T) { + containerStatus := v1.ContainerStatus{ + Name: "test-container", + Ready: true, + State: v1.ContainerState{ + Running: &v1.ContainerStateRunning{ + StartedAt: metav1.Time{Time: time.Now()}, + }, + }, + } + + podStatus := PodStatus{ + PodName: "test-pod", + PodUID: "12345-67890", + PodNamespace: "default", + JobID: "slurm-123456", + Containers: []v1.ContainerStatus{containerStatus}, + } + + // Serialize to JSON + data, err := json.Marshal(podStatus) + require.NoError(t, err) + assert.NotEmpty(t, data) + + // Deserialize from JSON + var decoded PodStatus + err = json.Unmarshal(data, &decoded) + require.NoError(t, err) + + assert.Equal(t, podStatus.PodName, decoded.PodName) + assert.Equal(t, podStatus.PodUID, decoded.PodUID) + assert.Equal(t, podStatus.PodNamespace, decoded.PodNamespace) + assert.Equal(t, podStatus.JobID, decoded.JobID) + assert.Len(t, decoded.Containers, 1) + assert.Equal(t, "test-container", decoded.Containers[0].Name) +} + +func TestCreateStruct_JSONSerialization(t *testing.T) { + createStruct := CreateStruct{ + PodUID: "pod-uuid-12345", + PodJID: "slurm-job-67890", + } + + // Serialize to JSON + data, err := json.Marshal(createStruct) + require.NoError(t, err) + assert.NotEmpty(t, data) + + // Deserialize from JSON + var decoded CreateStruct + err = json.Unmarshal(data, &decoded) + require.NoError(t, err) + + assert.Equal(t, createStruct.PodUID, decoded.PodUID) + assert.Equal(t, createStruct.PodJID, decoded.PodJID) +} + +func TestRetrievedPodData_JSONSerialization(t *testing.T) { + pod := v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "retrieved-pod", + Namespace: "default", + }, + } + + container := RetrievedContainer{ + Name: "main-container", + ConfigMaps: []v1.ConfigMap{ + { + ObjectMeta: metav1.ObjectMeta{Name: "config1"}, + Data: map[string]string{"key": "value"}, + }, + }, + Secrets: []v1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{Name: "secret1"}, + Data: map[string][]byte{"password": []byte("secret")}, + }, + }, + EmptyDirs: []string{"/tmp/empty1", "/tmp/empty2"}, + } + + jobScriptConfig := ScriptBuildConfig{ + SingularityHub: SingularityHubConfig{ + Server: "https://hub.example.com", + MasterToken: "token123", + }, + ApptainerOptions: ApptainerOptions{ + Executable: "/usr/bin/apptainer", + Fakeroot: true, + }, + } + + retrievedPod := RetrievedPodData{ + Pod: pod, + Containers: []RetrievedContainer{container}, + JobScriptBuild: jobScriptConfig, + JobScript: "#!/bin/bash\necho 'test'", + } + + // Serialize to JSON + data, err := json.Marshal(retrievedPod) + require.NoError(t, err) + assert.NotEmpty(t, data) + + // Deserialize from JSON + var decoded RetrievedPodData + err = json.Unmarshal(data, &decoded) + require.NoError(t, err) + + assert.Equal(t, retrievedPod.Pod.Name, decoded.Pod.Name) + assert.Len(t, decoded.Containers, 1) + assert.Equal(t, "main-container", decoded.Containers[0].Name) + assert.Len(t, decoded.Containers[0].ConfigMaps, 1) + assert.Len(t, decoded.Containers[0].Secrets, 1) + assert.Len(t, decoded.Containers[0].EmptyDirs, 2) + assert.Equal(t, retrievedPod.JobScript, decoded.JobScript) +} + +func TestLogStruct_JSONSerialization(t *testing.T) { + logOpts := ContainerLogOpts{ + Tail: 100, + LimitBytes: 1024, + Timestamps: true, + Follow: false, + Previous: false, + SinceSeconds: 3600, + SinceTime: time.Now(), + } + + logStruct := LogStruct{ + Namespace: "default", + PodUID: "pod-12345", + PodName: "test-pod", + ContainerName: "test-container", + Opts: logOpts, + } + + // Serialize to JSON + data, err := json.Marshal(logStruct) + require.NoError(t, err) + assert.NotEmpty(t, data) + + // Deserialize from JSON + var decoded LogStruct + err = json.Unmarshal(data, &decoded) + require.NoError(t, err) + + assert.Equal(t, logStruct.Namespace, decoded.Namespace) + assert.Equal(t, logStruct.PodUID, decoded.PodUID) + assert.Equal(t, logStruct.PodName, decoded.PodName) + assert.Equal(t, logStruct.ContainerName, decoded.ContainerName) + assert.Equal(t, logStruct.Opts.Tail, decoded.Opts.Tail) + assert.Equal(t, logStruct.Opts.LimitBytes, decoded.Opts.LimitBytes) + assert.Equal(t, logStruct.Opts.Timestamps, decoded.Opts.Timestamps) + assert.Equal(t, logStruct.Opts.Follow, decoded.Opts.Follow) +} + +func TestContainerLogOpts_DefaultValues(t *testing.T) { + // Test zero values + opts := ContainerLogOpts{} + + assert.Equal(t, 0, opts.Tail) + assert.Equal(t, 0, opts.LimitBytes) + assert.False(t, opts.Timestamps) + assert.False(t, opts.Follow) + assert.False(t, opts.Previous) + assert.Equal(t, 0, opts.SinceSeconds) +} + +func TestRetrievedContainer_EmptyDirsDeprecation(t *testing.T) { + // Test that EmptyDirs field still works (backwards compatibility) + container := RetrievedContainer{ + Name: "test", + EmptyDirs: []string{"/path1", "/path2"}, + } + + data, err := json.Marshal(container) + require.NoError(t, err) + + var decoded RetrievedContainer + err = json.Unmarshal(data, &decoded) + require.NoError(t, err) + + assert.Equal(t, container.EmptyDirs, decoded.EmptyDirs) + assert.Len(t, decoded.EmptyDirs, 2) +} + +func TestPodStatus_MultipleContainers(t *testing.T) { + podStatus := PodStatus{ + PodName: "multi-container-pod", + PodUID: "uuid-123", + PodNamespace: "default", + JobID: "job-456", + Containers: []v1.ContainerStatus{ + {Name: "container1", Ready: true}, + {Name: "container2", Ready: false}, + }, + InitContainers: []v1.ContainerStatus{ + {Name: "init1", Ready: true}, + }, + } + + data, err := json.Marshal(podStatus) + require.NoError(t, err) + + var decoded PodStatus + err = json.Unmarshal(data, &decoded) + require.NoError(t, err) + + assert.Len(t, decoded.Containers, 2) + assert.Len(t, decoded.InitContainers, 1) + assert.Equal(t, "container1", decoded.Containers[0].Name) + assert.Equal(t, "init1", decoded.InitContainers[0].Name) +} diff --git a/pkg/virtualkubelet/config_test.go b/pkg/virtualkubelet/config_test.go new file mode 100644 index 00000000..5413a41a --- /dev/null +++ b/pkg/virtualkubelet/config_test.go @@ -0,0 +1,120 @@ +package virtualkubelet + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConfig_DefaultValues(t *testing.T) { + config := Config{} + + assert.Empty(t, config.InterlinkURL) + assert.Empty(t, config.InterlinkPort) + assert.False(t, config.VerboseLogging) + assert.False(t, config.ErrorsOnlyLogging) + assert.False(t, config.DisableProjectedVolumes) +} + +func TestTLSConfig_Structure(t *testing.T) { + tlsConfig := TLSConfig{ + Enabled: true, + CertFile: "/path/to/cert.pem", + KeyFile: "/path/to/key.pem", + CACertFile: "/path/to/ca.pem", + } + + assert.True(t, tlsConfig.Enabled) + assert.Equal(t, "/path/to/cert.pem", tlsConfig.CertFile) + assert.Equal(t, "/path/to/key.pem", tlsConfig.KeyFile) + assert.Equal(t, "/path/to/ca.pem", tlsConfig.CACertFile) +} + +func TestResources_Configuration(t *testing.T) { + resources := Resources{ + CPU: "100", + Memory: "128Gi", + Pods: "100", + Accelerators: []Accelerator{ + { + ResourceType: "nvidia.com/gpu", + Model: "A100", + Available: 8, + }, + }, + } + + assert.Equal(t, "100", resources.CPU) + assert.Equal(t, "128Gi", resources.Memory) + assert.Equal(t, "100", resources.Pods) + assert.Len(t, resources.Accelerators, 1) + assert.Equal(t, "nvidia.com/gpu", resources.Accelerators[0].ResourceType) + assert.Equal(t, 8, resources.Accelerators[0].Available) +} + +func TestTaintSpec_Configuration(t *testing.T) { + taint := TaintSpec{ + Key: "virtual-node", + Value: "interlink", + Effect: "NoSchedule", + } + + assert.Equal(t, "virtual-node", taint.Key) + assert.Equal(t, "interlink", taint.Value) + assert.Equal(t, "NoSchedule", taint.Effect) +} + +func TestPodCIDR_Configuration(t *testing.T) { + podCIDR := PodCIDR{ + Subnet: "10.10.0.0/24", + MaxIP: 250, + MinIP: 2, + } + + assert.Equal(t, "10.10.0.0/24", podCIDR.Subnet) + assert.Equal(t, 250, podCIDR.MaxIP) + assert.Equal(t, 2, podCIDR.MinIP) +} + +func TestHTTP_SecuritySettings(t *testing.T) { + tests := []struct { + name string + http HTTP + insecure bool + }{ + { + name: "secure HTTP with CA cert", + http: HTTP{ + Insecure: false, + CaCert: "/path/to/ca.crt", + }, + insecure: false, + }, + { + name: "insecure HTTP", + http: HTTP{ + Insecure: true, + }, + insecure: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.insecure, tt.http.Insecure) + }) + } +} + +func TestNetwork_Configuration(t *testing.T) { + network := Network{ + EnableTunnel: true, + WildcardDNS: "*.example.com", + WstunnelTemplatePath: "/path/to/template", + WstunnelCommand: "wstunnel client --remote-addr %s", + } + + assert.True(t, network.EnableTunnel) + assert.Equal(t, "*.example.com", network.WildcardDNS) + assert.NotEmpty(t, network.WstunnelCommand) +} diff --git a/pkg/virtualkubelet/execute_test.go b/pkg/virtualkubelet/execute_test.go new file mode 100644 index 00000000..d251d2ba --- /dev/null +++ b/pkg/virtualkubelet/execute_test.go @@ -0,0 +1,197 @@ +package virtualkubelet + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetSidecarEndpoint(t *testing.T) { + ctx := context.Background() + tests := []struct { + name string + interlinkURL string + interlinkPort string + expected string + }{ + { + name: "HTTP URL", + interlinkURL: "http://localhost", + interlinkPort: "3000", + expected: "http://localhost:3000", + }, + { + name: "HTTPS URL", + interlinkURL: "https://interlink-api.example.com", + interlinkPort: "8443", + expected: "https://interlink-api.example.com:8443", + }, + { + name: "Unix socket", + interlinkURL: "unix:///var/run/interlink.sock", + interlinkPort: "", + expected: "http://unix", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getSidecarEndpoint(ctx, tt.interlinkURL, tt.interlinkPort) + assert.Equal(t, tt.expected, got) + }) + } +} + +func TestCreateTLSHTTPClient(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + tlsConfig TLSConfig + expectTLS bool + wantErr bool + }{ + { + name: "TLS disabled", + tlsConfig: TLSConfig{ + Enabled: false, + }, + expectTLS: false, + wantErr: false, + }, + { + name: "TLS enabled without certs", + tlsConfig: TLSConfig{ + Enabled: true, + }, + expectTLS: true, + wantErr: false, + }, + { + name: "TLS enabled with non-existent CA cert", + tlsConfig: TLSConfig{ + Enabled: true, + CACertFile: "/non/existent/ca.crt", + }, + expectTLS: false, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client, err := createTLSHTTPClient(ctx, tt.tlsConfig) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + assert.NotNil(t, client) + + if !tt.expectTLS { + // Default client has no custom transport + assert.Equal(t, http.DefaultClient, client) + } else { + // Custom client with TLS transport + assert.NotEqual(t, http.DefaultClient, client) + } + }) + } +} + +func TestDoRequestWithClient(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Verify headers + assert.Equal(t, "application/json", r.Header.Get("Content-Type")) + + authHeader := r.Header.Get("Authorization") + if authHeader != "" { + assert.Contains(t, authHeader, "Bearer") + } + + w.WriteHeader(http.StatusOK) + if _, err := w.Write([]byte(`{"status":"ok"}`)); err != nil { + panic(err) + } + })) + defer testServer.Close() + + tests := []struct { + name string + token string + wantErr bool + }{ + { + name: "request without token", + token: "", + wantErr: false, + }, + { + name: "request with token", + token: "test-token-123", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req, err := http.NewRequest(http.MethodGet, testServer.URL, nil) + require.NoError(t, err) + + client := testServer.Client() + resp, err := doRequestWithClient(req, tt.token, client) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + assert.NotNil(t, resp) + assert.Equal(t, http.StatusOK, resp.StatusCode) + resp.Body.Close() + }) + } +} + +func TestAddSessionContext(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/test", nil) + sessionID := "test-session-123" + + AddSessionContext(req, sessionID) + + got := req.Header.Get("InterLink-Http-Session") + assert.Equal(t, sessionID, got) +} + +func TestGetSessionContextMessage(t *testing.T) { + tests := []struct { + name string + sessionContext string + expected string + }{ + { + name: "normal session context", + sessionContext: "CreatePod#12345", + expected: "HTTP InterLink session CreatePod#12345: ", + }, + { + name: "empty session context", + sessionContext: "", + expected: "HTTP InterLink session : ", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := GetSessionContextMessage(tt.sessionContext) + assert.Equal(t, tt.expected, got) + }) + } +} diff --git a/plugins/slurm b/plugins/slurm new file mode 160000 index 00000000..e2a1cab1 --- /dev/null +++ b/plugins/slurm @@ -0,0 +1 @@ +Subproject commit e2a1cab131bf1e040d81e903dbdb3c0d2d578c81 diff --git a/systemd/user/.slurm-plugin.service.swp b/systemd/user/.slurm-plugin.service.swp deleted file mode 100644 index d0d24e88..00000000 Binary files a/systemd/user/.slurm-plugin.service.swp and /dev/null differ diff --git a/systemd/user/interlink.service b/systemd/user/interlink.service deleted file mode 100644 index 777101db..00000000 --- a/systemd/user/interlink.service +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description=This Unit is needed to automatically start the interLink API at system startup -After=network.target - -[Service] -Type=simple -Restart=always -RestartSec=3 - -ExecStart=/home/USERNAME/.interlink/bin/interlink -Environment="INTERLINKCONFIGPATH=/home/USERNAME/.interlink/config/InterLinkConfig.yaml" -Environment="SHARED_FS=true" - -Environment="ENABLE_TRACING=0" - -StandardOutput=append:/home/USERNAME/.interlink/logs/interlink.log -StandardError=append:/home/USERNAME/.interlink/logs/interlink.log - -[Install] -WantedBy=multi-user.target diff --git a/systemd/user/oauth2-proxy.cfg b/systemd/user/oauth2-proxy.cfg deleted file mode 100644 index 78997b3b..00000000 --- a/systemd/user/oauth2-proxy.cfg +++ /dev/null @@ -1,27 +0,0 @@ -client_id = "YOUR OIDC CLIENT ID" -client_secret = "YOUR OIDC CLIENT SECRET" -http_address = "0.0.0.0:30443" -oidc_issuer_url = "https://iam.cloud.infn.it/" -pass_authorization_header = "true" -provider = "oidc" -redirect_url = "http://localhost:8081/" -oidc_extra_audiences = [ "users" ] -upstreams = [ "unix:///leonardo/home/usera07cna/a07cna01/.interlink/interlink.sock", "http://localhost:30080/"] -allowed_groups = [ "YOUR USER SUB"] -validate_url = "https://iam.cloud.infn.it/token" -oidc_groups_claim = "sub" -email_domains = [ - "*" - ] -cookie_secret= "2IS..nfCSKy4=" -skip_auth_routes = [ - "='*'" - ] -force_https = "true" -https_address = "0.0.0.0:30443" -tls_cert_file = "/home/USERNAME/.interlink/config/tls.crt" -tls_key_file = "/home/USERNAME/.interlink/config/tls.key" - -tls_cipher_suites = ["TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256","TLS_RSA_WITH_AES_128_CBC_SHA","TLS_RSA_WITH_AES_128_GCM_SHA256","TLS_RSA_WITH_AES_256_CBC_SHA","TLS_RSA_WITH_AES_256_GCM_SHA384"] -skip_jwt_bearer_tokens = "true" - diff --git a/systemd/user/oauth2-proxy.service b/systemd/user/oauth2-proxy.service deleted file mode 100644 index 4a2e295c..00000000 --- a/systemd/user/oauth2-proxy.service +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description=This Unit is needed to automatically start the oauth2-proxy at system startup -After=network.target - -[Service] -Type=simple -Restart=always -RestartSec=3 - -WorkDirectory=/home/USERNAME/.interlink/config/ - -ExecStart=/home/USERNAME/.interlink/bin/oauth2-proxy --config=/home/USERNAME/.config/systemd/user/oauth2-proxy.cfg - -StandardOutput=append:/home/USERNAME/interlink/logs/oauth2-proxy.log -StandardError=append:/home/USERNAME/.interlink/logs/oauth2-proxy.log - -[Install] -WantedBy=multi-user.target - - diff --git a/systemd/user/slurm-plugin.service b/systemd/user/slurm-plugin.service deleted file mode 100644 index 3abce749..00000000 --- a/systemd/user/slurm-plugin.service +++ /dev/null @@ -1,19 +0,0 @@ -[Unit] -Description=This Unit is needed to automatically start the SLURM plugin at system startup -After=network.target - -[Service] -Type=simple -Restart=always -RestartSec=3 - -ExecStart=/home/USERNAME/.interlink/bin/plugin -Environment="SLURMCONFIGPATH=/home/USERNAME/.interlink/config/plugin-config.yaml" -Environment="SHARED_FS=true" - -Environment="ENABLE_TRACING=0" -StandardOutput=append:/home/USERNAME/.interlink/logs/plugin.log -StandardError=append:/home/USERNAME/.interlink/logs/plugin.log - -[Install] -WantedBy=multi-user.target diff --git a/test/vk-test-set b/test/vk-test-set new file mode 160000 index 00000000..c62a98fe --- /dev/null +++ b/test/vk-test-set @@ -0,0 +1 @@ +Subproject commit c62a98febef21c60af354d6f2cf745b358ae0a37