github.com/projecteru2/core@v0.0.0-20240321043226-06bcc1c23f58/cluster/calcium/lambda_test.go (about)

     1  package calcium
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"testing"
     9  
    10  	enginemocks "github.com/projecteru2/core/engine/mocks"
    11  	enginetypes "github.com/projecteru2/core/engine/types"
    12  	lockmocks "github.com/projecteru2/core/lock/mocks"
    13  	resourcemocks "github.com/projecteru2/core/resource/mocks"
    14  	plugintypes "github.com/projecteru2/core/resource/plugins/types"
    15  	resourcetypes "github.com/projecteru2/core/resource/types"
    16  	storemocks "github.com/projecteru2/core/store/mocks"
    17  	"github.com/projecteru2/core/strategy"
    18  	"github.com/projecteru2/core/types"
    19  	walmocks "github.com/projecteru2/core/wal/mocks"
    20  
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/mock"
    23  )
    24  
    25  func TestRunAndWaitFailedThenWALCommitted(t *testing.T) {
    26  	assert := assert.New(t)
    27  	c, _ := newCreateWorkloadCluster(t)
    28  
    29  	rmgr := &resourcemocks.Manager{}
    30  	rmgr.On("GetNodeResourceInfo", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil, nil, nil)
    31  	rmgr.On("GetNodesDeployCapacity", mock.Anything, mock.Anything, mock.Anything).Return(
    32  		nil, 0, types.ErrMockError,
    33  	)
    34  	c.rmgr = rmgr
    35  
    36  	mwal := c.wal.(*walmocks.WAL)
    37  	defer mwal.AssertNotCalled(t, "Log")
    38  	mwal.On("Log", mock.Anything, mock.Anything).Return(nil, nil)
    39  
    40  	opts := &types.DeployOptions{
    41  		Name:           "zc:name",
    42  		Count:          2,
    43  		DeployStrategy: strategy.Auto,
    44  		Podname:        "p1",
    45  		Resources:      resourcetypes.Resources{},
    46  		Image:          "zc:test",
    47  		Entrypoint: &types.Entrypoint{
    48  			Name: "good-entrypoint",
    49  		},
    50  		NodeFilter: &types.NodeFilter{},
    51  	}
    52  
    53  	_, ch, err := c.RunAndWait(context.Background(), opts, make(chan []byte))
    54  	assert.NoError(err)
    55  	assert.NotNil(ch)
    56  	ms := []*types.AttachWorkloadMessage{}
    57  	for m := range ch {
    58  		ms = append(ms, m)
    59  	}
    60  	m := ms[0]
    61  	assert.Equal(m.WorkloadID, "")
    62  	assert.True(strings.HasPrefix(string(m.Data), "Create workload failed"))
    63  
    64  	assert.Equal(m.StdStreamType, types.EruError)
    65  }
    66  
    67  func TestLambdaWithWorkloadIDReturned(t *testing.T) {
    68  	assert := assert.New(t)
    69  	c, nodes := newLambdaCluster(t)
    70  	engine := nodes[0].Engine.(*enginemocks.API)
    71  	store := c.store.(*storemocks.Store)
    72  	workload := &types.Workload{ID: "workloadfortonictest", Engine: engine}
    73  	store.On("GetWorkload", mock.Anything, mock.Anything).Return(workload, nil)
    74  	store.On("GetWorkloads", mock.Anything, mock.Anything).Return([]*types.Workload{workload}, nil)
    75  
    76  	opts := &types.DeployOptions{
    77  		Name:           "zc:name",
    78  		Count:          2,
    79  		DeployStrategy: strategy.Auto,
    80  		Podname:        "p1",
    81  		Resources:      resourcetypes.Resources{},
    82  		Image:          "zc:test",
    83  		Entrypoint: &types.Entrypoint{
    84  			Name: "good-entrypoint",
    85  		},
    86  		NodeFilter: &types.NodeFilter{},
    87  	}
    88  
    89  	r1, w1 := io.Pipe()
    90  	go func() {
    91  		w1.Write([]byte("stdout line1\n"))
    92  		w1.Write([]byte("stdout line2\n"))
    93  		w1.Close()
    94  	}()
    95  	r2, w2 := io.Pipe()
    96  	go func() {
    97  		w2.Write([]byte("stderr line1\n"))
    98  		w2.Write([]byte("stderr line2\n"))
    99  		w2.Close()
   100  	}()
   101  	engine.On("VirtualizationLogs", mock.Anything, mock.Anything).Return(io.NopCloser(r1), io.NopCloser(r2), nil)
   102  	engine.On("VirtualizationWait", mock.Anything, mock.Anything, mock.Anything).Return(&enginetypes.VirtualizationWaitResult{Code: 0}, nil)
   103  
   104  	ids, ch, err := c.RunAndWait(context.Background(), opts, make(chan []byte))
   105  	assert.NoError(err)
   106  	assert.NotNil(ch)
   107  	assert.Equal(len(ids), 2)
   108  	assert.Equal(ids[0], "workloadfortonictest")
   109  
   110  	ms := []*types.AttachWorkloadMessage{}
   111  	for m := range ch {
   112  		ms = append(ms, m)
   113  	}
   114  	assert.Equal(len(ms), 6)
   115  	assert.True(strings.HasPrefix(string(ms[5].Data), exitDataPrefix))
   116  	assert.Equal(ms[5].StdStreamType, types.Stdout)
   117  }
   118  
   119  func TestLambdaWithError(t *testing.T) {
   120  	assert := assert.New(t)
   121  	c, nodes := newLambdaCluster(t)
   122  	engine := nodes[0].Engine.(*enginemocks.API)
   123  
   124  	workload := &types.Workload{ID: "workloadfortonictest", Engine: engine}
   125  	store := c.store.(*storemocks.Store)
   126  	store.On("GetWorkloads", mock.Anything, mock.Anything).Return([]*types.Workload{workload}, nil)
   127  
   128  	opts := &types.DeployOptions{
   129  		Name:           "zc:name",
   130  		Count:          2,
   131  		DeployStrategy: strategy.Auto,
   132  		Podname:        "p1",
   133  		Resources:      resourcetypes.Resources{},
   134  		Image:          "zc:test",
   135  		Entrypoint: &types.Entrypoint{
   136  			Name: "good-entrypoint",
   137  		},
   138  		NodeFilter: &types.NodeFilter{},
   139  	}
   140  
   141  	store.On("GetWorkload", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("error")).Twice()
   142  	_, ch0, err := c.RunAndWait(context.Background(), opts, make(chan []byte))
   143  	assert.NoError(err)
   144  	assert.NotNil(ch0)
   145  	m0 := <-ch0
   146  	assert.Equal(m0.WorkloadID, "workloadfortonictest")
   147  	assert.True(strings.HasPrefix(string(m0.Data), "Get workload"))
   148  	assert.Equal(m0.StdStreamType, types.EruError)
   149  
   150  	store.On("GetWorkload", mock.Anything, mock.Anything).Return(workload, nil)
   151  
   152  	engine.On("VirtualizationLogs", mock.Anything, mock.Anything).Return(nil, nil, fmt.Errorf("error")).Twice()
   153  	_, ch1, err := c.RunAndWait(context.Background(), opts, make(chan []byte))
   154  	assert.NoError(err)
   155  	assert.NotNil(ch1)
   156  	m1 := <-ch1
   157  	assert.Equal(m1.WorkloadID, "workloadfortonictest")
   158  	assert.True(strings.HasPrefix(string(m1.Data), "Fetch log for workload"))
   159  	assert.Equal(m1.StdStreamType, types.EruError)
   160  
   161  	r1, w1 := io.Pipe()
   162  	go func() {
   163  		w1.Write([]byte("stdout line1\n"))
   164  		w1.Write([]byte("stdout line2\n"))
   165  		w1.Close()
   166  	}()
   167  	r2, w2 := io.Pipe()
   168  	go func() {
   169  		w2.Write([]byte("stderr line1\n"))
   170  		w2.Write([]byte("stderr line2\n"))
   171  		w2.Close()
   172  	}()
   173  	engine.On("VirtualizationLogs", mock.Anything, mock.Anything).Return(io.NopCloser(r1), io.NopCloser(r2), nil)
   174  
   175  	engine.On("VirtualizationWait", mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("error"))
   176  	ids, ch2, err := c.RunAndWait(context.Background(), opts, make(chan []byte))
   177  	assert.NoError(err)
   178  	assert.NotNil(ch2)
   179  	assert.Equal(ids[0], "workloadfortonictest")
   180  	assert.Equal(ids[1], "workloadfortonictest")
   181  
   182  	ms := []*types.AttachWorkloadMessage{}
   183  	for m := range ch2 {
   184  		ms = append(ms, m)
   185  	}
   186  	assert.Equal(len(ms), 6)
   187  	assert.Equal(ms[5].WorkloadID, "workloadfortonictest")
   188  	assert.True(strings.HasPrefix(string(ms[5].Data), "Wait workload"))
   189  	assert.Equal(ms[5].StdStreamType, types.EruError)
   190  }
   191  
   192  func newLambdaCluster(t *testing.T) (*Calcium, []*types.Node) {
   193  	c, nodes := newCreateWorkloadCluster(t)
   194  	node1, node2 := nodes[0], nodes[1]
   195  
   196  	store := c.store.(*storemocks.Store)
   197  	rmgr := c.rmgr.(*resourcemocks.Manager)
   198  	rmgr.On("GetNodeResourceInfo", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(
   199  		resourcetypes.Resources{},
   200  		resourcetypes.Resources{},
   201  		[]string{},
   202  		nil,
   203  	)
   204  	rmgr.On("GetNodesDeployCapacity", mock.Anything, mock.Anything, mock.Anything).Return(
   205  		map[string]*plugintypes.NodeDeployCapacity{
   206  			node1.Name: {
   207  				Capacity: 10,
   208  				Usage:    0.5,
   209  				Rate:     0.05,
   210  				Weight:   100,
   211  			},
   212  			node2.Name: {
   213  				Capacity: 10,
   214  				Usage:    0.5,
   215  				Rate:     0.05,
   216  				Weight:   100,
   217  			},
   218  		},
   219  		20, nil,
   220  	)
   221  	rmgr.On("Alloc", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(
   222  		[]resourcetypes.Resources{{}, {}},
   223  		[]resourcetypes.Resources{
   224  			{node1.Name: {}},
   225  			{node2.Name: {}},
   226  		},
   227  		nil,
   228  	)
   229  	store.On("GetDeployStatus", mock.Anything, mock.Anything, mock.Anything).Return(map[string]int{}, nil)
   230  	store.On("CreateProcessing", mock.Anything, mock.Anything, mock.Anything).Return(nil)
   231  	store.On("UpdateProcessing", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
   232  	store.On("DeleteProcessing", mock.Anything, mock.Anything, mock.Anything).Return(nil)
   233  
   234  	lock := &lockmocks.DistributedLock{}
   235  	lock.On("Lock", mock.Anything).Return(context.Background(), nil)
   236  	lock.On("Unlock", mock.Anything).Return(nil)
   237  	store.On("CreateLock", mock.Anything, mock.Anything).Return(lock, nil)
   238  	store.On("GetNodesByPod", mock.Anything, mock.Anything).Return(nodes, nil)
   239  	store.On("GetNode",
   240  		mock.AnythingOfType("*context.emptyCtx"),
   241  		mock.AnythingOfType("string"),
   242  	).Return(
   243  		func(_ context.Context, name string) (node *types.Node) {
   244  			node = node1
   245  			if name == "n2" {
   246  				node = node2
   247  			}
   248  			return
   249  		}, nil)
   250  
   251  	store.On("GetDeployStatus", mock.Anything, mock.Anything, mock.Anything).Return(map[string]int{}, nil)
   252  	old := strategy.Plans[strategy.Auto]
   253  	strategy.Plans[strategy.Auto] = func(ctx context.Context, sis []strategy.Info, need, total, _ int) (map[string]int, error) {
   254  		deployInfos := make(map[string]int)
   255  		for _, si := range sis {
   256  			deployInfos[si.Nodename] = 1
   257  		}
   258  		return deployInfos, nil
   259  	}
   260  	defer func() {
   261  		strategy.Plans[strategy.Auto] = old
   262  	}()
   263  
   264  	store.On("GetNode",
   265  		mock.AnythingOfType("*context.timerCtx"),
   266  		mock.AnythingOfType("string"),
   267  	).Return(
   268  		func(_ context.Context, name string) (node *types.Node) {
   269  			node = node1
   270  			if name == "n2" {
   271  				node = node2
   272  			}
   273  			return
   274  		}, nil)
   275  	engine := node1.Engine.(*enginemocks.API)
   276  
   277  	// doDeployOneWorkload fails: VirtualizationCreate
   278  	engine.On("ImageLocalDigests", mock.Anything, mock.Anything).Return([]string{""}, nil)
   279  	engine.On("ImageRemoteDigest", mock.Anything, mock.Anything).Return("", nil)
   280  
   281  	// doCreateAndStartWorkload fails: AddWorkload
   282  	engine.On("VirtualizationCreate", mock.Anything, mock.Anything).Return(&enginetypes.VirtualizationCreated{ID: "workloadfortonictest"}, nil)
   283  	engine.On("VirtualizationStart", mock.Anything, mock.Anything).Return(nil)
   284  	engine.On("VirtualizationRemove", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
   285  	store.On("ListNodeWorkloads", mock.Anything, mock.Anything, mock.Anything).Return(nil, types.ErrMockError)
   286  	engine.On("VirtualizationInspect", mock.Anything, mock.Anything).Return(&enginetypes.VirtualizationInfo{}, nil)
   287  	store.On("AddWorkload", mock.Anything, mock.Anything, mock.Anything).Return(nil)
   288  
   289  	return c, nodes
   290  }