github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/e2e/events/events.go (about)

     1  package events
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/hashicorp/nomad/api"
     8  	"github.com/hashicorp/nomad/e2e/e2eutil"
     9  	"github.com/hashicorp/nomad/e2e/framework"
    10  	"github.com/hashicorp/nomad/helper/uuid"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  	"github.com/hashicorp/nomad/testutil"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  type EventsTest struct {
    17  	framework.TC
    18  	jobIDs []string
    19  }
    20  
    21  func init() {
    22  	framework.AddSuites(&framework.TestSuite{
    23  		Component:   "Events",
    24  		CanRunLocal: true,
    25  		Cases: []framework.TestCase{
    26  			new(EventsTest),
    27  		},
    28  	})
    29  }
    30  
    31  func (tc *EventsTest) BeforeAll(f *framework.F) {
    32  	e2eutil.WaitForLeader(f.T(), tc.Nomad())
    33  }
    34  
    35  func (tc *EventsTest) AfterEach(f *framework.F) {
    36  	nomadClient := tc.Nomad()
    37  	j := nomadClient.Jobs()
    38  
    39  	for _, id := range tc.jobIDs {
    40  		j.Deregister(id, true, nil)
    41  	}
    42  	_, err := e2eutil.Command("nomad", "system", "gc")
    43  	f.NoError(err)
    44  }
    45  
    46  // TestDeploymentEvents registers a job then applies a change
    47  // An event stream listening to Deployment Events asserts that
    48  // a DeploymentPromotion event is emitted
    49  func (tc *EventsTest) TestDeploymentEvents(f *framework.F) {
    50  	t := f.T()
    51  
    52  	nomadClient := tc.Nomad()
    53  	events := nomadClient.EventStream()
    54  
    55  	uuid := uuid.Generate()
    56  	jobID := fmt.Sprintf("deployment-%s", uuid[0:8])
    57  	tc.jobIDs = append(tc.jobIDs, jobID)
    58  	ctx, cancel := context.WithCancel(context.Background())
    59  	defer cancel()
    60  
    61  	topics := map[api.Topic][]string{
    62  		api.TopicDeployment: {jobID},
    63  	}
    64  
    65  	var deployEvents []api.Event
    66  	streamCh, err := events.Stream(ctx, topics, 0, nil)
    67  	require.NoError(t, err)
    68  
    69  	// gather deployment events
    70  	go func() {
    71  		for {
    72  			select {
    73  			case <-ctx.Done():
    74  				return
    75  			case event := <-streamCh:
    76  				if event.IsHeartbeat() {
    77  					continue
    78  				}
    79  
    80  				deployEvents = append(deployEvents, event.Events...)
    81  			}
    82  		}
    83  	}()
    84  
    85  	// register job
    86  	e2eutil.RegisterAndWaitForAllocs(t, nomadClient, "events/input/initial.nomad", jobID, "")
    87  
    88  	// update job
    89  	e2eutil.RegisterAllocs(t, nomadClient, "events/input/deploy.nomad", jobID, "")
    90  
    91  	ds := e2eutil.DeploymentsForJob(t, nomadClient, jobID)
    92  	require.Equal(t, 2, len(ds))
    93  	deploy := ds[0]
    94  
    95  	// wait for deployment to be running and ready for auto promote
    96  	e2eutil.WaitForDeployment(t, nomadClient, deploy.ID, structs.DeploymentStatusRunning, structs.DeploymentStatusDescriptionRunningAutoPromotion)
    97  
    98  	// ensure there is a deployment promotion event
    99  	testutil.WaitForResult(func() (bool, error) {
   100  		for _, e := range deployEvents {
   101  			if e.Type == "DeploymentPromotion" {
   102  				return true, nil
   103  			}
   104  		}
   105  		var got []string
   106  		for _, e := range deployEvents {
   107  			got = append(got, e.Type)
   108  		}
   109  		return false, fmt.Errorf("expected to receive deployment promotion event, got: %#v", got)
   110  	}, func(e error) {
   111  		f.NoError(e)
   112  	})
   113  }
   114  
   115  // TestBlockedEvalEvents applies a job with a large memory requirement. The
   116  // event stream checks for a failed task group alloc
   117  func (tc *EventsTest) TestBlockedEvalEvents(f *framework.F) {
   118  	t := f.T()
   119  
   120  	nomadClient := tc.Nomad()
   121  	events := nomadClient.EventStream()
   122  
   123  	uuid := uuid.Generate()
   124  	jobID := fmt.Sprintf("blocked-deploy-%s", uuid[0:8])
   125  	tc.jobIDs = append(tc.jobIDs, jobID)
   126  	ctx, cancel := context.WithCancel(context.Background())
   127  	defer cancel()
   128  
   129  	topics := map[api.Topic][]string{
   130  		api.TopicEvaluation: {"*"},
   131  	}
   132  
   133  	var evalEvents []api.Event
   134  	streamCh, err := events.Stream(ctx, topics, 0, nil)
   135  	require.NoError(t, err)
   136  
   137  	// gather deployment events
   138  	go func() {
   139  		for {
   140  			select {
   141  			case <-ctx.Done():
   142  				return
   143  			case event := <-streamCh:
   144  				if event.IsHeartbeat() {
   145  					continue
   146  				}
   147  
   148  				evalEvents = append(evalEvents, event.Events...)
   149  			}
   150  		}
   151  	}()
   152  
   153  	// register job
   154  	e2eutil.Register(jobID, "events/input/large-job.nomad")
   155  
   156  	// ensure there is a deployment promotion event
   157  	testutil.WaitForResult(func() (bool, error) {
   158  		for _, e := range evalEvents {
   159  			eval, err := e.Evaluation()
   160  			if err != nil {
   161  				return false, fmt.Errorf("event was not an evaluation %w", err)
   162  			}
   163  
   164  			ftg := eval.FailedTGAllocs
   165  
   166  			tg, ok := ftg["one"]
   167  			if !ok {
   168  				continue
   169  			}
   170  
   171  			mem := tg.DimensionExhausted["memory"]
   172  			require.NotNil(t, mem, "memory dimension was nil")
   173  			require.Greater(t, mem, 0, "memory dimension was zero")
   174  			return true, nil
   175  
   176  		}
   177  		return false, fmt.Errorf("expected blocked eval with memory exhausted, got: %#v", evalEvents)
   178  	}, func(e error) {
   179  		require.NoError(t, e)
   180  	})
   181  }
   182  
   183  // TestStartIndex applies a job, then connects to the stream with a start
   184  // index to verify that the events from before the job are not included.
   185  func (tc *EventsTest) TestStartIndex(f *framework.F) {
   186  	t := f.T()
   187  
   188  	nomadClient := tc.Nomad()
   189  	events := nomadClient.EventStream()
   190  
   191  	uuid := uuid.Generate()
   192  	jobID := fmt.Sprintf("deployment-%s", uuid[0:8])
   193  	jobID2 := fmt.Sprintf("deployment2-%s", uuid[0:8])
   194  	tc.jobIDs = append(tc.jobIDs, jobID, jobID2)
   195  	ctx, cancel := context.WithCancel(context.Background())
   196  	defer cancel()
   197  
   198  	// register job
   199  	err := e2eutil.Register(jobID, "events/input/initial.nomad")
   200  	require.NoError(t, err)
   201  	job, _, err := nomadClient.Jobs().Info(jobID, nil)
   202  	require.NoError(t, err)
   203  	startIndex := *job.JobModifyIndex + 1
   204  
   205  	topics := map[api.Topic][]string{
   206  		api.TopicJob: {"*"},
   207  	}
   208  
   209  	// starting at Job.ModifyIndex + 1, the next (and only) JobRegistered event that we see
   210  	// should be from a different job registration
   211  	streamCh, err := events.Stream(ctx, topics, startIndex, nil)
   212  	require.NoError(t, err)
   213  
   214  	var jobEvents []api.Event
   215  	// gather job register events
   216  	go func() {
   217  		for {
   218  			select {
   219  			case <-ctx.Done():
   220  				return
   221  			case event, ok := <-streamCh:
   222  				if !ok {
   223  					return
   224  				}
   225  				if event.IsHeartbeat() {
   226  					continue
   227  				}
   228  				jobEvents = append(jobEvents, event.Events...)
   229  			}
   230  		}
   231  	}()
   232  
   233  	// new job (to make sure we get a JobRegistered event)
   234  	err = e2eutil.Register(jobID2, "events/input/deploy.nomad")
   235  	require.NoError(t, err)
   236  
   237  	// ensure there is a deployment promotion event
   238  	foundUnexpected := false
   239  	testutil.WaitForResult(func() (bool, error) {
   240  		for _, e := range jobEvents {
   241  			if e.Type == "JobRegistered" {
   242  				if e.Index <= startIndex {
   243  					foundUnexpected = true
   244  				}
   245  				if e.Index >= startIndex {
   246  					return true, nil
   247  				}
   248  			}
   249  		}
   250  		return false, fmt.Errorf("expected to receive JobRegistered event for index at least %v", startIndex)
   251  	}, func(e error) {
   252  		f.NoError(e)
   253  	})
   254  	require.False(t, foundUnexpected, "found events from earlier-than-expected indices")
   255  }