gopkg.in/hashicorp/nomad.v0@v0.11.8/nomad/scaling_endpoint_test.go (about)

     1  package nomad
     2  
     3  import (
     4  	msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
     5  	"github.com/stretchr/testify/require"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hashicorp/nomad/acl"
    10  	"github.com/hashicorp/nomad/helper/uuid"
    11  	"github.com/hashicorp/nomad/nomad/mock"
    12  	"github.com/hashicorp/nomad/nomad/structs"
    13  	"github.com/hashicorp/nomad/testutil"
    14  )
    15  
    16  func TestScalingEndpoint_GetPolicy(t *testing.T) {
    17  	t.Parallel()
    18  	require := require.New(t)
    19  
    20  	s1, cleanupS1 := TestServer(t, nil)
    21  	defer cleanupS1()
    22  	codec := rpcClient(t, s1)
    23  	testutil.WaitForLeader(t, s1.RPC)
    24  
    25  	p1 := mock.ScalingPolicy()
    26  	p2 := mock.ScalingPolicy()
    27  	s1.fsm.State().UpsertScalingPolicies(1000, []*structs.ScalingPolicy{p1, p2})
    28  
    29  	// Lookup the policy
    30  	get := &structs.ScalingPolicySpecificRequest{
    31  		ID: p1.ID,
    32  		QueryOptions: structs.QueryOptions{
    33  			Region: "global",
    34  		},
    35  	}
    36  	var resp structs.SingleScalingPolicyResponse
    37  	err := msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
    38  	require.NoError(err)
    39  	require.EqualValues(1000, resp.Index)
    40  	require.Equal(*p1, *resp.Policy)
    41  
    42  	// Lookup non-existing policy
    43  	get.ID = uuid.Generate()
    44  	resp = structs.SingleScalingPolicyResponse{}
    45  	err = msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
    46  	require.NoError(err)
    47  	require.EqualValues(1000, resp.Index)
    48  	require.Nil(resp.Policy)
    49  }
    50  
    51  func TestScalingEndpoint_GetPolicy_ACL(t *testing.T) {
    52  	t.Parallel()
    53  	require := require.New(t)
    54  
    55  	s1, root, cleanupS1 := TestACLServer(t, nil)
    56  	defer cleanupS1()
    57  	codec := rpcClient(t, s1)
    58  	testutil.WaitForLeader(t, s1.RPC)
    59  	state := s1.fsm.State()
    60  
    61  	p1 := mock.ScalingPolicy()
    62  	p2 := mock.ScalingPolicy()
    63  	state.UpsertScalingPolicies(1000, []*structs.ScalingPolicy{p1, p2})
    64  
    65  	get := &structs.ScalingPolicySpecificRequest{
    66  		ID: p1.ID,
    67  		QueryOptions: structs.QueryOptions{
    68  			Region: "global",
    69  		},
    70  	}
    71  
    72  	// lookup without token should fail
    73  	var resp structs.SingleScalingPolicyResponse
    74  	err := msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
    75  	require.Error(err)
    76  	require.Contains(err.Error(), "Permission denied")
    77  
    78  	// Expect failure for request with an invalid token
    79  	invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
    80  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListScalingPolicies}))
    81  	get.AuthToken = invalidToken.SecretID
    82  	err = msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
    83  	require.Error(err)
    84  	require.Contains(err.Error(), "Permission denied")
    85  	type testCase struct {
    86  		authToken string
    87  		name      string
    88  	}
    89  	cases := []testCase{
    90  		{
    91  			name:      "mgmt token should succeed",
    92  			authToken: root.SecretID,
    93  		},
    94  		{
    95  			name: "read disposition should succeed",
    96  			authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-read",
    97  				mock.NamespacePolicy(structs.DefaultNamespace, "read", nil)).SecretID,
    98  		},
    99  		{
   100  			name: "write disposition should succeed",
   101  			authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-write",
   102  				mock.NamespacePolicy(structs.DefaultNamespace, "write", nil)).SecretID,
   103  		},
   104  		{
   105  			name: "autoscaler disposition should succeed",
   106  			authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-autoscaler",
   107  				mock.NamespacePolicy(structs.DefaultNamespace, "scale", nil)).SecretID,
   108  		},
   109  		{
   110  			name: "list-jobs+read-job capability should succeed",
   111  			authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-read-job-scaling",
   112  				mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs, acl.NamespaceCapabilityReadJob})).SecretID,
   113  		},
   114  	}
   115  
   116  	for _, tc := range cases {
   117  		get.AuthToken = tc.authToken
   118  		err = msgpackrpc.CallWithCodec(codec, "Scaling.GetPolicy", get, &resp)
   119  		require.NoError(err, tc.name)
   120  		require.EqualValues(1000, resp.Index)
   121  		require.NotNil(resp.Policy)
   122  	}
   123  
   124  }
   125  
   126  func TestScalingEndpoint_ListPolicies(t *testing.T) {
   127  	t.Parallel()
   128  	require := require.New(t)
   129  
   130  	s1, cleanupS1 := TestServer(t, nil)
   131  	defer cleanupS1()
   132  	codec := rpcClient(t, s1)
   133  	testutil.WaitForLeader(t, s1.RPC)
   134  
   135  	// Lookup the policies
   136  	get := &structs.ScalingPolicyListRequest{
   137  		QueryOptions: structs.QueryOptions{
   138  			Region: "global",
   139  		},
   140  	}
   141  	var resp structs.ACLPolicyListResponse
   142  	err := msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", get, &resp)
   143  	require.NoError(err)
   144  	require.Empty(resp.Policies)
   145  
   146  	p1 := mock.ScalingPolicy()
   147  	p2 := mock.ScalingPolicy()
   148  	s1.fsm.State().UpsertScalingPolicies(1000, []*structs.ScalingPolicy{p1, p2})
   149  
   150  	err = msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", get, &resp)
   151  	require.NoError(err)
   152  	require.EqualValues(1000, resp.Index)
   153  	require.Len(resp.Policies, 2)
   154  }
   155  
   156  func TestScalingEndpoint_ListPolicies_ACL(t *testing.T) {
   157  	t.Parallel()
   158  	require := require.New(t)
   159  
   160  	s1, root, cleanupS1 := TestACLServer(t, nil)
   161  	defer cleanupS1()
   162  	codec := rpcClient(t, s1)
   163  	testutil.WaitForLeader(t, s1.RPC)
   164  	state := s1.fsm.State()
   165  
   166  	p1 := mock.ScalingPolicy()
   167  	p2 := mock.ScalingPolicy()
   168  	state.UpsertScalingPolicies(1000, []*structs.ScalingPolicy{p1, p2})
   169  
   170  	get := &structs.ScalingPolicyListRequest{
   171  		QueryOptions: structs.QueryOptions{
   172  			Region: "global",
   173  		},
   174  	}
   175  
   176  	// lookup without token should fail
   177  	var resp structs.ACLPolicyListResponse
   178  	err := msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", get, &resp)
   179  	require.Error(err)
   180  	require.Contains(err.Error(), "Permission denied")
   181  
   182  	// Expect failure for request with an invalid token
   183  	invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
   184  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListScalingPolicies}))
   185  	get.AuthToken = invalidToken.SecretID
   186  	require.Error(err)
   187  	require.Contains(err.Error(), "Permission denied")
   188  
   189  	type testCase struct {
   190  		authToken string
   191  		name      string
   192  	}
   193  	cases := []testCase{
   194  		{
   195  			name:      "mgmt token should succeed",
   196  			authToken: root.SecretID,
   197  		},
   198  		{
   199  			name: "read disposition should succeed",
   200  			authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-read",
   201  				mock.NamespacePolicy(structs.DefaultNamespace, "read", nil)).SecretID,
   202  		},
   203  		{
   204  			name: "write disposition should succeed",
   205  			authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-write",
   206  				mock.NamespacePolicy(structs.DefaultNamespace, "write", nil)).SecretID,
   207  		},
   208  		{
   209  			name: "autoscaler disposition should succeed",
   210  			authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-autoscaler",
   211  				mock.NamespacePolicy(structs.DefaultNamespace, "scale", nil)).SecretID,
   212  		},
   213  		{
   214  			name: "list-scaling-policies capability should succeed",
   215  			authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-list-scaling-policies",
   216  				mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListScalingPolicies})).SecretID,
   217  		},
   218  		{
   219  			name: "list-jobs+read-job capability should succeed",
   220  			authToken: mock.CreatePolicyAndToken(t, state, 1005, "test-valid-read-job-scaling",
   221  				mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs, acl.NamespaceCapabilityReadJob})).SecretID,
   222  		},
   223  	}
   224  
   225  	for _, tc := range cases {
   226  		get.AuthToken = tc.authToken
   227  		err = msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", get, &resp)
   228  		require.NoError(err, tc.name)
   229  		require.EqualValues(1000, resp.Index)
   230  		require.Len(resp.Policies, 2)
   231  	}
   232  }
   233  
   234  func TestScalingEndpoint_ListPolicies_Blocking(t *testing.T) {
   235  	t.Parallel()
   236  	require := require.New(t)
   237  
   238  	s1, cleanupS1 := TestServer(t, nil)
   239  	defer cleanupS1()
   240  	state := s1.fsm.State()
   241  	codec := rpcClient(t, s1)
   242  	testutil.WaitForLeader(t, s1.RPC)
   243  
   244  	// Create the policies
   245  	p1 := mock.ScalingPolicy()
   246  	p2 := mock.ScalingPolicy()
   247  
   248  	// First create an unrelated policy
   249  	time.AfterFunc(100*time.Millisecond, func() {
   250  		err := state.UpsertScalingPolicies(100, []*structs.ScalingPolicy{p1})
   251  		require.NoError(err)
   252  	})
   253  
   254  	// Upsert the policy we are watching later
   255  	time.AfterFunc(200*time.Millisecond, func() {
   256  		err := state.UpsertScalingPolicies(200, []*structs.ScalingPolicy{p2})
   257  		require.NoError(err)
   258  	})
   259  
   260  	// Lookup the policy
   261  	req := &structs.ScalingPolicyListRequest{
   262  		QueryOptions: structs.QueryOptions{
   263  			Region:        "global",
   264  			MinQueryIndex: 150,
   265  		},
   266  	}
   267  	var resp structs.ScalingPolicyListResponse
   268  	start := time.Now()
   269  	err := msgpackrpc.CallWithCodec(codec, "Scaling.ListPolicies", req, &resp)
   270  	require.NoError(err)
   271  
   272  	require.True(time.Since(start) > 200*time.Millisecond, "should block: %#v", resp)
   273  	require.EqualValues(200, resp.Index, "bad index")
   274  	require.Len(resp.Policies, 2)
   275  	require.ElementsMatch([]string{p1.ID, p2.ID}, []string{resp.Policies[0].ID, resp.Policies[1].ID})
   276  }