diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3a45493 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +/dagger.gen.go linguist-generated +/internal/dagger/** linguist-generated +/internal/querybuilder/** linguist-generated +/internal/telemetry/** linguist-generated diff --git a/.github/workflows/benchmark-pipeline.yaml b/.github/workflows/benchmark-pipeline.yaml index 9cb1188..90d99b8 100644 --- a/.github/workflows/benchmark-pipeline.yaml +++ b/.github/workflows/benchmark-pipeline.yaml @@ -45,90 +45,21 @@ jobs: echo "| benchmark_job_url | ${{ github.event.inputs.benchmark_job_url }} |" >> $GITHUB_STEP_SUMMARY echo "| benchmark_job_duration_mins | ${{ github.event.inputs.benchmark_job_duration_mins }} |" >> $GITHUB_STEP_SUMMARY - deploy: + benchmark: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - - uses: azure/setup-kubectl@v4 + - run: echo "${{ secrets.KUBECONFIG }}" > ./kube-config + - uses: dagger/dagger-for-github@v7 with: - version: v1.30.2 - id: install - - run: mkdir ~/.kube && echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config - - name: Select the manifest - run: | - MANIFEST=projects/${{ inputs.cncf_project }} - CONFIG=${{ inputs.config }} - if [[ -n $CONFIG ]]; then - echo "Configuration provided" - MANIFEST=$MANIFEST/$CONFIG.yaml - else - MANIFEST=$MANIFEST/${{ inputs.cncf_project }}.yaml - fi - - if ! test -f "$MANIFEST"; then - echo "The provided inputs are invalid." - exit 1 - fi - - export VERSION=${{ inputs.version }} - envsubst < $MANIFEST > manifest.yaml - - uses: actions/upload-artifact@v4 - with: - name: manifest - path: manifest.yaml - - name: Apply the manifest - run: | - kubectl apply -f manifest.yaml - - sleep 20 - - kubectl wait pod \ - --all \ - --for=condition=Ready \ - --namespace=benchmark - - benchmark-job: - runs-on: ubuntu-24.04 - needs: deploy - steps: - - uses: actions/checkout@v4 - - uses: azure/setup-kubectl@v4 - with: - version: v1.30.2 - id: install - - run: mkdir ~/.kube && echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config - - name: Run the benchmark job - run: | - kubectl apply -f ${{ inputs.benchmark_job_url }} - - sleep 20 - - kubectl wait pod \ - --all \ - --for=condition=Ready \ - --namespace=falco # TODO: Revert to "benchmark" this after merging https://github.com/falcosecurity/cncf-green-review-testing/pull/22 - - - name: Wait for the benchmark job to complete - run: | - sleep ${{ inputs.benchmark_job_duration_mins }}m - - - name: Delete the benchmark job - run: | - kubectl delete -f ${{ inputs.benchmark_job_url }} --wait - - delete: - runs-on: ubuntu-24.04 - needs: benchmark-job - if: ${{ always() }} - steps: - - uses: actions/checkout@v4 - - uses: azure/setup-kubectl@v4 - with: - version: v1.30.2 - id: install - - run: mkdir ~/.kube && echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config - - - uses: actions/download-artifact@v4 - with: - name: manifest - - run: kubectl delete -f manifest.yaml --wait + module: "." + version: "0.15.1" + args: benchmark-pipeline + --source='.' + --kubeconfig='/src/kube-config' + --cncf-project='${{ inputs.cncf_project }}' + --config='${{ inputs.config }}' + --version='${{ inputs.version }}' + --benchmark-job-url='${{ inputs.benchmark_job_url }}' + --benchmark-job-duration-mins=${{ inputs.benchmark_job_duration_mins }} + - run: rm ./kube-config diff --git a/.gitignore b/.gitignore index 488b566..d8b6cc0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ infrastructure/equinix-metal/.terraform/ infrastructure/equinix-metal/terraform.tfvars infrastructure/equinix-metal/.terraform.lock.hcl .idea +/dagger.gen.go +/internal/dagger +/internal/querybuilder +/internal/telemetry diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b25e626 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.21 + +RUN apk add ca-certificates kubectl --no-cache diff --git a/dagger.json b/dagger.json new file mode 100644 index 0000000..60e8246 --- /dev/null +++ b/dagger.json @@ -0,0 +1,6 @@ +{ + "name": "green-reviews-tooling", + "engineVersion": "v0.15.1", + "sdk": "go", + "source": "." +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0fec2d6 --- /dev/null +++ b/go.mod @@ -0,0 +1,51 @@ +module github.com/cncf-tags/green-reviews-tooling + +go 1.23.2 + +require ( + github.com/99designs/gqlgen v0.17.57 + github.com/Khan/genqlient v0.7.0 + github.com/vektah/gqlparser/v2 v2.5.19 + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 + go.opentelemetry.io/otel/log v0.3.0 + go.opentelemetry.io/otel/metric v1.27.0 + go.opentelemetry.io/otel/sdk v1.27.0 + go.opentelemetry.io/otel/sdk/log v0.3.0 + go.opentelemetry.io/otel/sdk/metric v1.27.0 + go.opentelemetry.io/otel/trace v1.27.0 + go.opentelemetry.io/proto/otlp v1.3.1 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/sync v0.10.0 + google.golang.org/grpc v1.68.0 +) + +require ( + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/go-logr/logr v1.4.2 // 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.22.0 // indirect + github.com/sosodev/duration v1.3.1 // indirect + github.com/stretchr/testify v1.10.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/protobuf v1.35.2 // indirect +) + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 + +replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.3.0 + +replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.3.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e566804 --- /dev/null +++ b/go.sum @@ -0,0 +1,85 @@ +github.com/99designs/gqlgen v0.17.57 h1:Ak4p60BRq6QibxY0lEc0JnQhDurfhxA67sp02lMjmPc= +github.com/99designs/gqlgen v0.17.57/go.mod h1:Jx61hzOSTcR4VJy/HFIgXiQ5rJ0Ypw8DxWLjbYDAUw0= +github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w= +github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM= +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/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/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/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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +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/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= +github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= +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.19 h1:bhCPCX1D4WWzCDvkPl4+TP1N8/kLrWnp43egplt7iSg= +github.com/vektah/gqlparser/v2 v2.5.19/go.mod h1:y7kvl5bBlDeuWIvLtA9849ncyvx6/lj06RsMrEjVy3U= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 h1:oM0GTNKGlc5qHctWeIGTVyda4iFFalOzMZ3Ehj5rwB4= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88/go.mod h1:JGG8ebaMO5nXOPnvKEl+DiA4MGwFjCbjsxT1WHIEBPY= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 h1:ccBrA8nCY5mM0y5uO7FT0ze4S0TuFcWdDB2FxGMTjkI= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0/go.mod h1:/9pb6634zi2Lk8LYg9Q0X8Ar6jka4dkFOylBLbVQPCE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= +go.opentelemetry.io/otel/log v0.3.0 h1:kJRFkpUFYtny37NQzL386WbznUByZx186DpEMKhEGZs= +go.opentelemetry.io/otel/log v0.3.0/go.mod h1:ziCwqZr9soYDwGNbIL+6kAvQC+ANvjgG367HVcyR/ys= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/log v0.3.0 h1:GEjJ8iftz2l+XO1GF2856r7yYVh74URiF9JMcAacr5U= +go.opentelemetry.io/otel/sdk/log v0.3.0/go.mod h1:BwCxtmux6ACLuys1wlbc0+vGBd+xytjmjajwqqIul2g= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +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.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..50d0812 --- /dev/null +++ b/main.go @@ -0,0 +1,64 @@ +// Dagger module for GreenReviewsTooling functions. + +package main + +import ( + "context" + "os" + + "github.com/cncf-tags/green-reviews-tooling/internal/dagger" + "github.com/cncf-tags/green-reviews-tooling/pkg/pipeline" +) + +type GreenReviewsTooling struct{} + +// BenchmarkPipeline measures the sustainability footprint of CNCF projects. +func (m *GreenReviewsTooling) BenchmarkPipeline(ctx context.Context, + source *dagger.Directory, + cncfProject, + // +optional + config, + version, + benchmarkJobURL, + kubeconfig string, + benchmarkJobDurationMins int) (*dagger.Container, error) { + p, err := newPipeline(source, kubeconfig) + if err != nil { + return nil, err + } + + return p.Benchmark(ctx, cncfProject, config, version, benchmarkJobURL, benchmarkJobDurationMins) +} + +func newPipeline(source *dagger.Directory, kubeconfig string) (*pipeline.Pipeline, error) { + var configFile *dagger.File + var err error + + container := build(source) + configFile, err = getKubeconfig(kubeconfig) + if err != nil { + return nil, err + } + + return pipeline.New(container, source, configFile) +} + +func build(src *dagger.Directory) *dagger.Container { + return dag.Container(). + WithDirectory("/src", src). + Directory("/src"). + DockerBuild(). + WithMountedDirectory("/src", src). + WithWorkdir("/src") +} + +func getKubeconfig(configFilePath string) (*dagger.File, error) { + contents, err := os.ReadFile(configFilePath) + if err != nil { + return nil, err + } + + filePath := "/.kube/config" + dir := dag.Directory().WithNewFile(filePath, string(contents)) + return dir.File(filePath), nil +} diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go new file mode 100644 index 0000000..c216fe2 --- /dev/null +++ b/pkg/cmd/cmd.go @@ -0,0 +1,35 @@ +package cmd + +func Apply(manifest string) []string { + return []string{ + "kubectl", + "apply", + "-f", + manifest, + } +} + +func Delete(manifest string) []string { + return []string{ + "kubectl", + "delete", + "-f", + manifest, + "--wait", + } +} + +func WaitForNamespace(namespace string) []string { + return []string{ + "kubectl", + "wait", + "pod", + "--all", + "--namespace", + namespace, + "--timeout", + "300s", + "--for", + "condition=Ready", + } +} diff --git a/pkg/pipeline/benchmark.go b/pkg/pipeline/benchmark.go new file mode 100644 index 0000000..96606ee --- /dev/null +++ b/pkg/pipeline/benchmark.go @@ -0,0 +1,193 @@ +package pipeline + +import ( + "context" + "fmt" + "log" + "path" + "path/filepath" + "strings" + "time" + + "github.com/cncf-tags/green-reviews-tooling/internal/dagger" + "github.com/cncf-tags/green-reviews-tooling/pkg/cmd" +) + +const ( + benchmarkNamespace = "benchmark" + falcoNamespace = "falco" // TODO Remove when no longer used. +) + +type Pipeline struct { + container *dagger.Container + dir *dagger.Directory + kubeconfig *dagger.File +} + +func New(container *dagger.Container, dir *dagger.Directory, kubeconfig *dagger.File) (*Pipeline, error) { + return &Pipeline{ + container: container, + dir: dir, + kubeconfig: kubeconfig, + }, nil +} + +// Benchmark measures the sustainability footprint of CNCF projects. +func (p *Pipeline) Benchmark(ctx context.Context, + cncfProject, + config, + version, + benchmarkJobURL string, + benchmarkJobDurationMins int) (*dagger.Container, error) { + _, err := p.benchmark(ctx, cncfProject, config, version, benchmarkJobURL, benchmarkJobDurationMins) + if err != nil { + log.Printf("benchmark failed: %v", err) + } + + _, err = p.delete(ctx, cncfProject, config, benchmarkJobURL) + if err != nil { + return nil, err + } + + return p.container, nil +} + +func (p *Pipeline) Terminal(ctx context.Context) (*dagger.Container, error) { + return p.withKubeconfig().Terminal(), nil +} + +func (p *Pipeline) benchmark(ctx context.Context, + cncfProject, + config, + version, + benchmarkJobURL string, + benchmarkJobDurationMins int) (*dagger.Container, error) { + // Create CNCF project resources. + _, err := p.deploy(ctx, cncfProject, config, version) + if err != nil { + return nil, err + } + + // Create benchmark job resources. + _, err = p.exec(ctx, cmd.Apply(benchmarkJobURL)) + if err != nil { + return nil, err + } + + // Wait for pods to be ready. + _, err = p.exec(ctx, cmd.WaitForNamespace(benchmarkNamespace)) + if err != nil { + return nil, err + } + + // TODO Remove once benchmark job resources created in benchmark namespace. + _, err = p.exec(ctx, cmd.WaitForNamespace(falcoNamespace)) + if err != nil { + return nil, err + } + + p.echo(ctx, fmt.Sprintf("waiting %d minutes for benchmark to complete", benchmarkJobDurationMins)) + + time.Sleep(time.Duration(benchmarkJobDurationMins) * time.Minute) + + p.echo(ctx, "benchmark complete") + + return p.container, nil +} + +func (p *Pipeline) delete(ctx context.Context, cncfProject, config, benchmarkJobURL string) (*dagger.Container, error) { + // Delete benchmark job resources. + _, err := p.exec(ctx, cmd.Delete(benchmarkJobURL)) + if err != nil { + log.Printf("failed to delete benchmark job: %v", err) + } + + fileName, fileContents, err := p.getManifestFile(ctx, cncfProject, config, "") + if err != nil { + return nil, err + } + + // Delete CNCF project resources. + _, err = p.execWithNewFile(ctx, fileName, fileContents, cmd.Delete(fileName)) + if err != nil { + return nil, err + } + + return p.container, nil +} + +func (p *Pipeline) deploy(ctx context.Context, cncfProject, config, version string) (*dagger.Container, error) { + fileName, fileContents, err := p.getManifestFile(ctx, cncfProject, config, version) + if err != nil { + return nil, err + } + + _, err = p.execWithNewFile(ctx, fileName, fileContents, cmd.Apply(fileName)) + if err != nil { + return nil, err + } + + // Allow time for pods to be created. + time.Sleep(15 * time.Second) + + _, err = p.exec(ctx, cmd.WaitForNamespace(benchmarkNamespace)) + if err != nil { + return nil, err + } + + return p.container, nil +} + +func (p *Pipeline) echo(ctx context.Context, msg string) (string, error) { + // Bust cache to ensure commands are run. + return p.container.WithEnvVariable("BUST_CACHE", time.Now().String()). + WithExec([]string{"echo", fmt.Sprintf("'%s'", msg)}). + Stdout(ctx) +} + +func (p *Pipeline) exec(ctx context.Context, args []string) (string, error) { + return p.withKubeconfig(). + WithExec(args). + Stdout(ctx) +} + +func (p *Pipeline) execWithDir(ctx context.Context, manifestPath string, args []string) (string, error) { + dirPath := path.Dir(manifestPath) + dir := p.dir.Directory(dirPath) + return p.withKubeconfig(). + WithDirectory(dirPath, dir). + WithExec(args). + Stdout(ctx) +} + +func (p *Pipeline) execWithNewFile(ctx context.Context, name, contents string, args []string) (string, error) { + return p.withKubeconfig(). + WithNewFile(name, contents). + WithExec(args). + Stdout(ctx) +} + +func (p *Pipeline) getManifestFile(ctx context.Context, project, config, version string) (string, string, error) { + manifestPath := getManifestPath(project, config) + manifest, err := p.dir.File(manifestPath).Contents(ctx) + if err != nil { + return "", "", err + } + + return "/tmp/manifest.yaml", strings.ReplaceAll(manifest, "$VERSION", version), nil +} + +func (p *Pipeline) withKubeconfig() *dagger.Container { + return p.container. + // Bust cache to ensure commands are run. + WithEnvVariable("BUST_CACHE", time.Now().String()). + WithEnvVariable("KUBECONFIG", "/.kube/config"). + WithFile("/.kube/config", p.kubeconfig) +} + +func getManifestPath(project, config string) string { + if config == "" { + config = project + } + return filepath.Join("/projects", project, fmt.Sprintf("%s.yaml", config)) +}