github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/agent/testutils/fakes.go (about)

     1  package testutils
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"net"
     7  	"os"
     8  	"path/filepath"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"google.golang.org/grpc"
    14  
    15  	"github.com/docker/swarmkit/agent/exec"
    16  	"github.com/docker/swarmkit/api"
    17  	"github.com/docker/swarmkit/ca"
    18  	"github.com/docker/swarmkit/identity"
    19  	"github.com/docker/swarmkit/log"
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  // TestExecutor is executor for integration tests
    24  type TestExecutor struct {
    25  	mu   sync.Mutex
    26  	desc *api.NodeDescription
    27  }
    28  
    29  // Describe just returns empty NodeDescription.
    30  func (e *TestExecutor) Describe(ctx context.Context) (*api.NodeDescription, error) {
    31  	e.mu.Lock()
    32  	defer e.mu.Unlock()
    33  	if e.desc == nil {
    34  		return &api.NodeDescription{}, nil
    35  	}
    36  	return e.desc.Copy(), nil
    37  }
    38  
    39  // Configure does nothing.
    40  func (e *TestExecutor) Configure(ctx context.Context, node *api.Node) error {
    41  	return nil
    42  }
    43  
    44  // SetNetworkBootstrapKeys does nothing.
    45  func (e *TestExecutor) SetNetworkBootstrapKeys([]*api.EncryptionKey) error {
    46  	return nil
    47  }
    48  
    49  // Controller returns TestController.
    50  func (e *TestExecutor) Controller(t *api.Task) (exec.Controller, error) {
    51  	return &TestController{
    52  		ch: make(chan struct{}),
    53  	}, nil
    54  }
    55  
    56  // UpdateNodeDescription sets the node description on the test executor
    57  func (e *TestExecutor) UpdateNodeDescription(newDesc *api.NodeDescription) {
    58  	e.mu.Lock()
    59  	defer e.mu.Unlock()
    60  	e.desc = newDesc
    61  }
    62  
    63  // TestController is dummy channel based controller for tests.
    64  type TestController struct {
    65  	ch        chan struct{}
    66  	closeOnce sync.Once
    67  }
    68  
    69  // Update does nothing.
    70  func (t *TestController) Update(ctx context.Context, task *api.Task) error {
    71  	return nil
    72  }
    73  
    74  // Prepare does nothing.
    75  func (t *TestController) Prepare(ctx context.Context) error {
    76  	return nil
    77  }
    78  
    79  // Start does nothing.
    80  func (t *TestController) Start(ctx context.Context) error {
    81  	return nil
    82  }
    83  
    84  // Wait waits on internal channel.
    85  func (t *TestController) Wait(ctx context.Context) error {
    86  	select {
    87  	case <-t.ch:
    88  	case <-ctx.Done():
    89  	}
    90  	return nil
    91  }
    92  
    93  // Shutdown closes internal channel
    94  func (t *TestController) Shutdown(ctx context.Context) error {
    95  	t.closeOnce.Do(func() {
    96  		close(t.ch)
    97  	})
    98  	return nil
    99  }
   100  
   101  // Terminate closes internal channel if it wasn't closed before.
   102  func (t *TestController) Terminate(ctx context.Context) error {
   103  	t.closeOnce.Do(func() {
   104  		close(t.ch)
   105  	})
   106  	return nil
   107  }
   108  
   109  // Remove does nothing.
   110  func (t *TestController) Remove(ctx context.Context) error {
   111  	return nil
   112  }
   113  
   114  // Close does nothing.
   115  func (t *TestController) Close() error {
   116  	t.closeOnce.Do(func() {
   117  		close(t.ch)
   118  	})
   119  	return nil
   120  }
   121  
   122  // SessionHandler is an injectable function that can be used handle session requests
   123  type SessionHandler func(*api.SessionRequest, api.Dispatcher_SessionServer) error
   124  
   125  // MockDispatcher is a fake dispatcher that one agent at a time can connect to
   126  type MockDispatcher struct {
   127  	mu             sync.Mutex
   128  	sessionCh      chan *api.SessionMessage
   129  	openSession    *api.SessionRequest
   130  	closedSessions []*api.SessionRequest
   131  	sessionHandler SessionHandler
   132  
   133  	Addr string
   134  }
   135  
   136  // UpdateTaskStatus is not implemented
   137  func (m *MockDispatcher) UpdateTaskStatus(context.Context, *api.UpdateTaskStatusRequest) (*api.UpdateTaskStatusResponse, error) {
   138  	panic("not implemented")
   139  }
   140  
   141  // Tasks keeps an open stream until canceled
   142  func (m *MockDispatcher) Tasks(_ *api.TasksRequest, stream api.Dispatcher_TasksServer) error {
   143  	<-stream.Context().Done()
   144  	return nil
   145  }
   146  
   147  // Assignments keeps an open stream until canceled
   148  func (m *MockDispatcher) Assignments(_ *api.AssignmentsRequest, stream api.Dispatcher_AssignmentsServer) error {
   149  	<-stream.Context().Done()
   150  	return nil
   151  }
   152  
   153  // Heartbeat always successfully heartbeats
   154  func (m *MockDispatcher) Heartbeat(context.Context, *api.HeartbeatRequest) (*api.HeartbeatResponse, error) {
   155  	return &api.HeartbeatResponse{Period: time.Second * 5}, nil
   156  }
   157  
   158  // Session allows a session to be established, and sends the node info
   159  func (m *MockDispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_SessionServer) error {
   160  	m.mu.Lock()
   161  	handler := m.sessionHandler
   162  	m.openSession = r
   163  	m.mu.Unlock()
   164  	sessionID := identity.NewID()
   165  
   166  	defer func() {
   167  		m.mu.Lock()
   168  		defer m.mu.Unlock()
   169  		log.G(stream.Context()).Debugf("non-dispatcher side closed session: %s", sessionID)
   170  		m.closedSessions = append(m.closedSessions, r)
   171  		if m.openSession == r { // only overwrite session if it hasn't changed
   172  			m.openSession = nil
   173  		}
   174  	}()
   175  
   176  	if handler != nil {
   177  		return handler(r, stream)
   178  	}
   179  
   180  	// send the initial message first
   181  	if err := stream.Send(&api.SessionMessage{
   182  		SessionID: sessionID,
   183  		Managers: []*api.WeightedPeer{
   184  			{
   185  				Peer: &api.Peer{Addr: m.Addr},
   186  			},
   187  		},
   188  	}); err != nil {
   189  		return err
   190  	}
   191  
   192  	ctx := stream.Context()
   193  	for {
   194  		select {
   195  		case msg := <-m.sessionCh:
   196  			msg.SessionID = sessionID
   197  			if err := stream.Send(msg); err != nil {
   198  				return err
   199  			}
   200  		case <-ctx.Done():
   201  			return nil
   202  		}
   203  	}
   204  }
   205  
   206  // GetSessions return all the established and closed sessions
   207  func (m *MockDispatcher) GetSessions() (*api.SessionRequest, []*api.SessionRequest) {
   208  	m.mu.Lock()
   209  	defer m.mu.Unlock()
   210  	return m.openSession, m.closedSessions
   211  }
   212  
   213  // SessionMessageChannel returns a writable channel to inject session messages
   214  func (m *MockDispatcher) SessionMessageChannel() chan<- *api.SessionMessage {
   215  	return m.sessionCh
   216  }
   217  
   218  // SetSessionHandler lets you inject a custom function to handle session requests
   219  func (m *MockDispatcher) SetSessionHandler(s SessionHandler) {
   220  	m.mu.Lock()
   221  	defer m.mu.Unlock()
   222  	m.sessionHandler = s
   223  }
   224  
   225  // NewMockDispatcher starts and returns a mock dispatcher instance that can be connected to
   226  func NewMockDispatcher(t *testing.T, secConfig *ca.SecurityConfig, local bool) (*MockDispatcher, func()) {
   227  	var (
   228  		l       net.Listener
   229  		err     error
   230  		addr    string
   231  		cleanup func()
   232  	)
   233  	if local {
   234  		tempDir, err := ioutil.TempDir("", "local-dispatcher-socket")
   235  		require.NoError(t, err)
   236  		addr = filepath.Join(tempDir, "socket")
   237  		l, err = net.Listen("unix", addr)
   238  		require.NoError(t, err)
   239  		cleanup = func() {
   240  			os.RemoveAll(tempDir)
   241  		}
   242  	} else {
   243  		l, err = net.Listen("tcp", "127.0.0.1:0")
   244  		require.NoError(t, err)
   245  		addr = l.Addr().String()
   246  	}
   247  
   248  	serverOpts := []grpc.ServerOption{grpc.Creds(secConfig.ServerTLSCreds)}
   249  	s := grpc.NewServer(serverOpts...)
   250  
   251  	m := &MockDispatcher{
   252  		Addr:      addr,
   253  		sessionCh: make(chan *api.SessionMessage, 1),
   254  	}
   255  	api.RegisterDispatcherServer(s, m)
   256  	go s.Serve(l)
   257  	return m, func() {
   258  		l.Close()
   259  		s.Stop()
   260  		if cleanup != nil {
   261  			cleanup()
   262  		}
   263  	}
   264  }