@@ -2,9 +2,16 @@ package e2e_test
22
33import (
44 "context"
5+ "crypto/sha256"
6+ "encoding/hex"
57 "fmt"
8+ "io"
69 "log/slog"
10+ "net/http"
711 "os"
12+ "os/exec"
13+ "path/filepath"
14+ "runtime"
815 "slices"
916 "strings"
1017 "testing"
@@ -116,10 +123,12 @@ func getCharts() []helmChart {
116123
117124func TestMain (m * testing.M ) {
118125 charts := getCharts ()
126+
119127 commonSetupFuncs := []env.Func {
120128 // we uninstall here as a defensive check but nothing should be left behind
121129 uninstallHelmRepos (charts ),
122130 installHelmRepos (charts ),
131+ installKubectlRuntimeEnforcer (),
123132 }
124133
125134 commonFinishFuncs := []env.Func {
@@ -257,3 +266,157 @@ func installHelmRepos(charts []helmChart) env.Func {
257266 return ctx , nil
258267 }
259268}
269+
270+ // installKubectlRuntimeEnforcer downloads and installs the kubectl runtime-enforcer plugin
271+ func installKubectlRuntimeEnforcer () env.Func {
272+ return func (ctx context.Context , config * envconf.Config ) (context.Context , error ) {
273+ // runtime-enforcer version
274+ version := "v0.6.0"
275+ installDir := "/usr/local/bin"
276+
277+ logger := slog .New (slog .NewJSONHandler (os .Stdout , nil ))
278+ goos := runtime .GOOS
279+ goarch := runtime .GOARCH
280+
281+ if ! isSupportedPlatform (goos , goarch ) {
282+ return ctx , fmt .Errorf ("unsupported platform: %s/%s (supported: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64)" , goos , goarch )
283+ }
284+
285+ logger .InfoContext (ctx , "Downloading and installing kubectl runtime-enforcer plugin." )
286+
287+ pluginName := "kubectl-runtime_enforcer"
288+ binaryName := fmt .Sprintf ("%s-%s-%s" , pluginName , goos , goarch )
289+ downloadURL := fmt .Sprintf ("https://github.com/rancher-sandbox/runtime-enforcer/releases/download/%s/%s" , version , binaryName )
290+ checksumURL := fmt .Sprintf ("%s.sha256" , downloadURL )
291+
292+ tempDir , err := os .MkdirTemp ("" , "runtime-enforcer-install-" )
293+ if err != nil {
294+ return ctx , fmt .Errorf ("failed to create temp directory: %w" , err )
295+ }
296+ defer os .RemoveAll (tempDir )
297+
298+ binaryPath := filepath .Join (tempDir , binaryName )
299+ checksumPath := filepath .Join (tempDir , binaryName + ".sha256" )
300+
301+ if err := downloadFile (downloadURL , binaryPath ); err != nil {
302+ return ctx , fmt .Errorf ("failed to download binary: %w" , err )
303+ }
304+
305+ if err := downloadFile (checksumURL , checksumPath ); err != nil {
306+ return ctx , fmt .Errorf ("failed to download checksum: %w" , err )
307+ }
308+
309+ if err := verifyChecksum (binaryPath , checksumPath ); err != nil {
310+ return ctx , fmt .Errorf ("checksum verification failed: %w" , err )
311+ }
312+
313+ if err := os .Chmod (binaryPath , 0755 ); err != nil {
314+ return ctx , fmt .Errorf ("failed to make binary executable: %w" , err )
315+ }
316+
317+ installPath := filepath .Join (installDir , pluginName )
318+ if err := moveFile (binaryPath , installPath ); err != nil {
319+ return ctx , fmt .Errorf ("failed to install binary: %w" , err )
320+ }
321+
322+ cmd := exec .Command (pluginName , "-v" )
323+ if output , err := cmd .CombinedOutput (); err != nil {
324+ return ctx , fmt .Errorf ("installation verification failed: %w\n Output: %s" , err , string (output ))
325+ }
326+
327+ return ctx , nil
328+ }
329+ }
330+
331+ func isSupportedPlatform (goos , goarch string ) bool {
332+ supported := map [string ][]string {
333+ "linux" : {"amd64" , "arm64" },
334+ "darwin" : {"amd64" , "arm64" },
335+ }
336+
337+ if archs , ok := supported [goos ]; ok {
338+ for _ , arch := range archs {
339+ if arch == goarch {
340+ return true
341+ }
342+ }
343+ }
344+ return false
345+ }
346+
347+ func downloadFile (url , filepath string ) error {
348+ resp , err := http .Get (url )
349+ if err != nil {
350+ return err
351+ }
352+ defer resp .Body .Close ()
353+
354+ if resp .StatusCode != http .StatusOK {
355+ return fmt .Errorf ("bad status: %s" , resp .Status )
356+ }
357+
358+ out , err := os .Create (filepath )
359+ if err != nil {
360+ return err
361+ }
362+ defer out .Close ()
363+
364+ _ , err = io .Copy (out , resp .Body )
365+ return err
366+ }
367+
368+ func verifyChecksum (binaryPath , checksumPath string ) error {
369+ checksumData , err := os .ReadFile (checksumPath )
370+ if err != nil {
371+ return err
372+ }
373+
374+ var expected string
375+ fmt .Sscanf (string (checksumData ), "%s" , & expected )
376+
377+ file , err := os .Open (binaryPath )
378+ if err != nil {
379+ return err
380+ }
381+ defer file .Close ()
382+
383+ hash := sha256 .New ()
384+ if _ , err := io .Copy (hash , file ); err != nil {
385+ return err
386+ }
387+ actual := hex .EncodeToString (hash .Sum (nil ))
388+
389+ if actual != expected {
390+ return fmt .Errorf ("checksum mismatch: expected %s, got %s" , expected , actual )
391+ }
392+
393+ return nil
394+ }
395+
396+ func moveFile (src , dst string ) error {
397+ if err := os .Rename (src , dst ); err == nil {
398+ return nil
399+ }
400+
401+ srcFile , err := os .Open (src )
402+ if err != nil {
403+ return err
404+ }
405+ defer srcFile .Close ()
406+
407+ dstFile , err := os .Create (dst )
408+ if err != nil {
409+ return err
410+ }
411+ defer dstFile .Close ()
412+
413+ if _ , err := io .Copy (dstFile , srcFile ); err != nil {
414+ return err
415+ }
416+
417+ if err := dstFile .Sync (); err != nil {
418+ return err
419+ }
420+
421+ return os .Remove (src )
422+ }
0 commit comments