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 }