github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/pkg/test/devstack/pythonwasm_test.go (about)

     1  //go:build integration
     2  
     3  package devstack
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/filecoin-project/bacalhau/pkg/devstack"
    15  	"github.com/filecoin-project/bacalhau/pkg/docker"
    16  	"github.com/filecoin-project/bacalhau/pkg/ipfs"
    17  	"github.com/filecoin-project/bacalhau/pkg/logger"
    18  	"github.com/filecoin-project/bacalhau/pkg/node"
    19  	testutils "github.com/filecoin-project/bacalhau/pkg/test/utils"
    20  
    21  	cmd "github.com/filecoin-project/bacalhau/cmd/bacalhau"
    22  	_ "github.com/filecoin-project/bacalhau/pkg/logger"
    23  	"github.com/filecoin-project/bacalhau/pkg/requester/publicapi"
    24  	"github.com/filecoin-project/bacalhau/pkg/system"
    25  	"github.com/rs/zerolog/log"
    26  
    27  	"github.com/stretchr/testify/require"
    28  	"github.com/stretchr/testify/suite"
    29  )
    30  
    31  type DevstackPythonWASMSuite struct {
    32  	suite.Suite
    33  }
    34  
    35  // In order for 'go test' to run this suite, we need to create
    36  // a normal test function and pass our suite to suite.Run
    37  func TestDevstackPythonWASMSuite(t *testing.T) {
    38  	suite.Run(t, new(DevstackPythonWASMSuite))
    39  }
    40  
    41  // Before each test
    42  func (s *DevstackPythonWASMSuite) SetupTest() {
    43  	docker.MustHaveDocker(s.T())
    44  
    45  	logger.ConfigureTestLogging(s.T())
    46  	err := system.InitConfigForTesting(s.T())
    47  	require.NoError(s.T(), err)
    48  }
    49  
    50  // full end-to-end test of python/wasm:
    51  //
    52  // * use CLI to submit a python job with --deterministic set
    53  // * context (python file and requirements.txt) should be pinned to ipfs by
    54  //   requester node
    55  // * docker executor downloads context and starts wasm container image with the
    56  //   context mounted in
    57  
    58  func (s *DevstackPythonWASMSuite) TestPythonWasmVolumes() {
    59  	testutils.SkipIfArm(s.T(), "https://github.com/filecoin-project/bacalhau/issues/1268")
    60  	cmd.Fatal = cmd.FakeFatalErrorHandler
    61  
    62  	nodeCount := 1
    63  	inputPath := "/input"
    64  	outputPath := "/output"
    65  	fileContents := "pineapples"
    66  
    67  	ctx := context.Background()
    68  	stack, _ := testutils.SetupTest(ctx, s.T(), nodeCount, 0, false,
    69  		node.NewComputeConfigWithDefaults(),
    70  		node.NewRequesterConfigWithDefaults())
    71  
    72  	tmpDir := s.T().TempDir()
    73  
    74  	oldDir, err := os.Getwd()
    75  	require.NoError(s.T(), err)
    76  	err = os.Chdir(tmpDir)
    77  	require.NoError(s.T(), err)
    78  	defer func() {
    79  		err := os.Chdir(oldDir)
    80  		require.NoError(s.T(), err)
    81  	}()
    82  
    83  	fileCid, err := ipfs.AddTextToNodes(ctx, []byte(fileContents), devstack.ToIPFSClients(stack.Nodes[:nodeCount])...)
    84  	require.NoError(s.T(), err)
    85  
    86  	// write bytes to main.py
    87  	mainPy := []byte(fmt.Sprintf(`
    88  	import os
    89  	print("LIST /")
    90  	print(os.listdir("/"))
    91  	print("LIST %s")
    92  	print(os.listdir("%s"))
    93  	open("%s/test.txt", "w").write(open("%s").read())
    94  `, outputPath, outputPath, outputPath, inputPath))
    95  
    96  	err = os.WriteFile("main.py", mainPy, 0644)
    97  	require.NoError(s.T(), err)
    98  
    99  	_, out, err := cmd.ExecuteTestCobraCommand(s.T(),
   100  		fmt.Sprintf("--api-port=%d", stack.Nodes[0].APIServer.Port),
   101  		"--api-host=localhost",
   102  		"run",
   103  		"-v", fmt.Sprintf("%s:%s", fileCid, inputPath),
   104  		"-o", fmt.Sprintf("%s:%s", "output", outputPath),
   105  		"python",
   106  		"--deterministic",
   107  		"main.py",
   108  	)
   109  	jobID := system.FindJobIDInTestOutput(out)
   110  	require.NoError(s.T(), err)
   111  	log.Debug().Msgf("jobId=%s", jobID)
   112  	time.Sleep(time.Second * 5)
   113  
   114  	node := stack.Nodes[0]
   115  	apiUri := node.APIServer.GetURI()
   116  	apiClient := publicapi.NewRequesterAPIClient(apiUri)
   117  	resolver := apiClient.GetJobStateResolver()
   118  	require.NoError(s.T(), err)
   119  	err = resolver.WaitUntilComplete(ctx, jobID)
   120  	require.NoError(s.T(), err)
   121  
   122  	shards, err := resolver.GetShards(ctx, jobID)
   123  	require.NoError(s.T(), err)
   124  	require.True(s.T(), len(shards) > 0)
   125  
   126  	shard := shards[0]
   127  
   128  	outputDir := s.T().TempDir()
   129  	require.NotEmpty(s.T(), shard.PublishedResult.CID)
   130  
   131  	finalOutputPath := filepath.Join(outputDir, shard.PublishedResult.CID)
   132  	err = node.IPFSClient.Get(ctx, shard.PublishedResult.CID, finalOutputPath)
   133  	require.NoError(s.T(), err)
   134  
   135  	err = filepath.Walk(finalOutputPath,
   136  		func(path string, info os.FileInfo, err error) error {
   137  			require.NoError(s.T(), err)
   138  			log.Debug().Msgf("%s - %d", path, info.Size())
   139  			return err
   140  		})
   141  	require.NoError(s.T(), err)
   142  
   143  	stdoutContents, err := os.ReadFile(filepath.Join(finalOutputPath, "stdout"))
   144  	require.NoError(s.T(), err)
   145  	require.NotEmpty(s.T(), stdoutContents)
   146  
   147  	log.Debug().Msgf("stdoutContents=> %s", stdoutContents)
   148  
   149  	// stderrContents, err := ioutil.ReadFile(filepath.Join(finalOutputPath, "stderr"))
   150  	// require.NoError(s.T(), err)
   151  	// require.Empty(s.T(), stderrContents, "stderr should be empty: %s", stderrContents)
   152  
   153  	filePath := fmt.Sprintf("%s/output/test.txt", finalOutputPath)
   154  	outputData, err := os.ReadFile(filePath)
   155  	require.NoError(s.T(), err)
   156  
   157  	require.Equal(s.T(), fileContents, strings.TrimSpace(string(outputData)))
   158  }
   159  func (s *DevstackPythonWASMSuite) TestSimplestPythonWasmDashC() {
   160  	testutils.SkipIfArm(s.T(), "https://github.com/filecoin-project/bacalhau/issues/1268")
   161  	cmd.Fatal = cmd.FakeFatalErrorHandler
   162  
   163  	ctx := context.Background()
   164  	stack, _ := testutils.SetupTest(ctx, s.T(), 1, 0, false,
   165  		node.NewComputeConfigWithDefaults(),
   166  		node.NewRequesterConfigWithDefaults())
   167  
   168  	// TODO: see also list_test.go, maybe factor out a common way to do this cli
   169  	// setup
   170  	_, out, err := cmd.ExecuteTestCobraCommand(s.T(),
   171  		fmt.Sprintf("--api-port=%d", stack.Nodes[0].APIServer.Port),
   172  		"--api-host=localhost",
   173  		"run",
   174  		"python",
   175  		"--deterministic",
   176  		"-c",
   177  		"print(1+1)",
   178  	)
   179  	require.NoError(s.T(), err)
   180  
   181  	jobId := system.FindJobIDInTestOutput(out)
   182  	require.NoError(s.T(), err)
   183  	log.Debug().Msgf("jobId=%s", jobId)
   184  	time.Sleep(time.Second * 5)
   185  
   186  	node := stack.Nodes[0]
   187  	apiUri := node.APIServer.GetURI()
   188  	apiClient := publicapi.NewRequesterAPIClient(apiUri)
   189  	resolver := apiClient.GetJobStateResolver()
   190  	require.NoError(s.T(), err)
   191  	err = resolver.WaitUntilComplete(ctx, jobId)
   192  	require.NoError(s.T(), err)
   193  
   194  }
   195  
   196  // TODO: test that > 10MB context is rejected
   197  
   198  func (s *DevstackPythonWASMSuite) TestSimplePythonWasm() {
   199  	testutils.SkipIfArm(s.T(), "https://github.com/filecoin-project/bacalhau/issues/1268")
   200  	cmd.Fatal = cmd.FakeFatalErrorHandler
   201  
   202  	ctx := context.Background()
   203  	stack, _ := testutils.SetupTest(ctx, s.T(), 1, 0, false,
   204  		node.NewComputeConfigWithDefaults(),
   205  		node.NewRequesterConfigWithDefaults())
   206  
   207  	tmpDir := s.T().TempDir()
   208  
   209  	oldDir, err := os.Getwd()
   210  	require.NoError(s.T(), err)
   211  	err = os.Chdir(tmpDir)
   212  	require.NoError(s.T(), err)
   213  	defer func() {
   214  		err := os.Chdir(oldDir)
   215  		require.NoError(s.T(), err)
   216  	}()
   217  
   218  	// write bytes to main.py
   219  	mainPy := []byte("print(1+1)\n")
   220  	err = os.WriteFile("main.py", mainPy, 0644)
   221  	require.NoError(s.T(), err)
   222  
   223  	_, out, err := cmd.ExecuteTestCobraCommand(s.T(),
   224  		fmt.Sprintf("--api-port=%d", stack.Nodes[0].APIServer.Port),
   225  		"--api-host=localhost",
   226  		"run",
   227  		"python",
   228  		"--deterministic",
   229  		"main.py",
   230  	)
   231  	require.NoError(s.T(), err)
   232  
   233  	jobId := system.FindJobIDInTestOutput(out)
   234  	require.NotEmpty(s.T(), jobId, "Unable to find Job ID in", out)
   235  	log.Debug().Msgf("jobId=%s", jobId)
   236  	time.Sleep(time.Second * 5)
   237  
   238  	apiUri := stack.Nodes[0].APIServer.GetURI()
   239  	apiClient := publicapi.NewRequesterAPIClient(apiUri)
   240  	resolver := apiClient.GetJobStateResolver()
   241  	require.NoError(s.T(), err)
   242  	err = resolver.WaitUntilComplete(ctx, jobId)
   243  	require.NoError(s.T(), err)
   244  }
   245  
   246  // func TestPythonWasmWithRequirements(t *testing.T) {
   247  // 	tmpDir, err := ioutil.TempDir("", "devstack_test")
   248  // 	require.NoError(t, err)
   249  // 	defer func() {
   250  // 		err := os.RemoveAll(tmpDir)
   251  // 		require.NoError(t, err)
   252  // 	}()
   253  
   254  // 	oldDir, err := os.Getwd()
   255  // 	require.NoError(t, err)
   256  // 	err = os.Chdir(tmpDir)
   257  // 	require.NoError(t, err)
   258  // 	defer func() {
   259  // 		err := os.Chdir(oldDir)
   260  // 		require.NoError(t, err)
   261  // 	}()
   262  
   263  // 	_, out, err := ExecuteTestCobraCommand(t, RootCmd,
   264  // 		"run",
   265  // 		"python",
   266  // 		"--deterministic",
   267  // 		"main.py",
   268  // 	)
   269  // 	require.NoError(t, err)
   270  
   271  // }