mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
Merge pull request #26291 from hashicorp/f-NMD-763-identity
identity: The initial implementation code for node identity.
This commit is contained in:
@@ -23,7 +23,7 @@ func TestACLBootstrapCommand(t *testing.T) {
|
||||
c.ACL.PolicyTTL = 0
|
||||
}
|
||||
|
||||
srv, _, url := testServer(t, true, config)
|
||||
srv, _, url := testServer(t, false, config)
|
||||
defer srv.Shutdown()
|
||||
|
||||
must.Nil(t, srv.RootToken)
|
||||
@@ -101,7 +101,7 @@ func TestACLBootstrapCommand_WithOperatorFileBootstrapToken(t *testing.T) {
|
||||
err := os.WriteFile(file, []byte(mockToken.SecretID), 0700)
|
||||
must.NoError(t, err)
|
||||
|
||||
srv, _, url := testServer(t, true, config)
|
||||
srv, _, url := testServer(t, false, config)
|
||||
defer srv.Shutdown()
|
||||
|
||||
must.Nil(t, srv.RootToken)
|
||||
@@ -139,7 +139,7 @@ func TestACLBootstrapCommand_WithBadOperatorFileBootstrapToken(t *testing.T) {
|
||||
err := os.WriteFile(file, []byte(invalidToken), 0700)
|
||||
must.NoError(t, err)
|
||||
|
||||
srv, _, url := testServer(t, true, config)
|
||||
srv, _, url := testServer(t, false, config)
|
||||
defer srv.Shutdown()
|
||||
|
||||
must.Nil(t, srv.RootToken)
|
||||
|
||||
@@ -1120,7 +1120,7 @@ func TestServer_Reload_TLS_Shared_Keyloader(t *testing.T) {
|
||||
TLSConfig: &config.TLSConfig{
|
||||
EnableHTTP: true,
|
||||
EnableRPC: true,
|
||||
VerifyServerHostname: true,
|
||||
VerifyServerHostname: false,
|
||||
CAFile: foocafile,
|
||||
CertFile: fooclientcert,
|
||||
KeyFile: fooclientkey,
|
||||
|
||||
@@ -450,6 +450,7 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
|
||||
s.mux.Handle("/v1/client/stats", wrapCORS(s.wrap(s.ClientStatsRequest)))
|
||||
s.mux.Handle("/v1/client/allocation/", wrapCORS(s.wrap(s.ClientAllocRequest)))
|
||||
s.mux.Handle("/v1/client/metadata", wrapCORS(s.wrap(s.NodeMetaRequest)))
|
||||
s.mux.Handle("/v1/client/identity/renew", wrapCORS(s.wrap(s.NodeIdentityRenewRequest)))
|
||||
|
||||
s.mux.HandleFunc("/v1/agent/self", s.wrap(s.AgentSelfRequest))
|
||||
s.mux.HandleFunc("/v1/agent/join", s.wrap(s.AgentJoinRequest))
|
||||
|
||||
43
command/agent/node_identity_endpoint.go
Normal file
43
command/agent/node_identity_endpoint.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
func (s *HTTPServer) NodeIdentityRenewRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
// Build the request by parsing all common parameters and node id
|
||||
args := structs.NodeIdentityRenewReq{}
|
||||
s.parse(resp, req, &args.QueryOptions.Region, &args.QueryOptions)
|
||||
parseNode(req, &args.NodeID)
|
||||
|
||||
// Determine the handler to use
|
||||
useLocalClient, useClientRPC, useServerRPC := s.rpcHandlerForNode(args.NodeID)
|
||||
|
||||
// Make the RPC
|
||||
var reply structs.NodeIdentityRenewResp
|
||||
var rpcErr error
|
||||
if useLocalClient {
|
||||
rpcErr = s.agent.Client().ClientRPC(structs.NodeIdentityRenewRPCMethod, &args, &reply)
|
||||
} else if useClientRPC {
|
||||
rpcErr = s.agent.Client().RPC(structs.NodeIdentityRenewRPCMethod, &args, &reply)
|
||||
} else if useServerRPC {
|
||||
rpcErr = s.agent.Server().RPC(structs.NodeIdentityRenewRPCMethod, &args, &reply)
|
||||
} else {
|
||||
rpcErr = CodedError(400, "no local Node and node_id not provided")
|
||||
}
|
||||
|
||||
if rpcErr != nil {
|
||||
if structs.IsErrNoNodeConn(rpcErr) {
|
||||
rpcErr = CodedError(404, rpcErr.Error())
|
||||
}
|
||||
|
||||
return nil, rpcErr
|
||||
}
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
@@ -639,6 +639,16 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"node identity": func() (cli.Command, error) {
|
||||
return &NodeIdentityCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"node identity renew": func() (cli.Command, error) {
|
||||
return &NodeIdentityRenewCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"node meta": func() (cli.Command, error) {
|
||||
return &NodeMetaCommand{
|
||||
Meta: meta,
|
||||
|
||||
34
command/node_identity.go
Normal file
34
command/node_identity.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/cli"
|
||||
)
|
||||
|
||||
type NodeIdentityCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (n *NodeIdentityCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: nomad node identity [subcommand]
|
||||
|
||||
Interact with a node's identity. All commands interact directly with a client
|
||||
and require setting the target node via its 36 character ID.
|
||||
|
||||
Please see the individual subcommand help for detailed usage information.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (n *NodeIdentityCommand) Synopsis() string { return "Force renewal of a nodes identity" }
|
||||
|
||||
func (n *NodeIdentityCommand) Name() string { return "node identity" }
|
||||
|
||||
func (n *NodeIdentityCommand) Run(_ []string) int {
|
||||
return cli.RunResultHelp
|
||||
}
|
||||
88
command/node_identity_renew.go
Normal file
88
command/node_identity_renew.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
type NodeIdentityRenewCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (n *NodeIdentityRenewCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: nomad node identity renew [options] <node_id>
|
||||
|
||||
Instruct a node to renew its identity at the next heartbeat. This command only
|
||||
applies to client agents.
|
||||
|
||||
General Options:
|
||||
|
||||
` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace)
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (n *NodeIdentityRenewCommand) Synopsis() string { return "Force a node to renew its identity" }
|
||||
|
||||
func (n *NodeIdentityRenewCommand) Name() string { return "node identity renew" }
|
||||
|
||||
func (n *NodeIdentityRenewCommand) Run(args []string) int {
|
||||
|
||||
flags := n.Meta.FlagSet(n.Name(), FlagSetClient)
|
||||
flags.Usage = func() { n.Ui.Output(n.Help()) }
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
args = flags.Args()
|
||||
|
||||
if len(args) != 1 {
|
||||
n.Ui.Error("This command takes one argument: <node_id>")
|
||||
n.Ui.Error(commandErrorText(n))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Get the HTTP client
|
||||
client, err := n.Meta.Client()
|
||||
if err != nil {
|
||||
n.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
nodeID := args[0]
|
||||
|
||||
// Lookup nodeID
|
||||
if nodeID != "" {
|
||||
nodeID, err = lookupNodeID(client.Nodes(), nodeID)
|
||||
if err != nil {
|
||||
n.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
req := api.NodeIdentityRenewRequest{
|
||||
NodeID: nodeID,
|
||||
}
|
||||
|
||||
if _, err := client.Nodes().Identity().Renew(&req, nil); err != nil {
|
||||
n.Ui.Error(fmt.Sprintf("Error requesting node identity renewal: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (n *NodeIdentityRenewCommand) AutocompleteFlags() complete.Flags {
|
||||
return n.Meta.AutocompleteFlags(FlagSetClient)
|
||||
}
|
||||
|
||||
func (n *NodeIdentityRenewCommand) AutocompleteArgs() complete.Predictor {
|
||||
return nodePredictor(n.Client, nil)
|
||||
}
|
||||
@@ -132,8 +132,18 @@ func (c *OperatorClientStateCommand) Run(args []string) int {
|
||||
Tasks: tasks,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the node identity state, which is useful when debugging to see the
|
||||
// real and current identity the node is using.
|
||||
nodeIdentity, err := db.GetNodeIdentity()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("failed to get node identity state: %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
output := debugOutput{
|
||||
Allocations: data,
|
||||
Allocations: data,
|
||||
NodeIdentity: nodeIdentity,
|
||||
}
|
||||
bytes, err := json.Marshal(output)
|
||||
if err != nil {
|
||||
@@ -146,7 +156,8 @@ func (c *OperatorClientStateCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
type debugOutput struct {
|
||||
Allocations map[string]*clientStateAlloc
|
||||
Allocations map[string]*clientStateAlloc
|
||||
NodeIdentity string
|
||||
}
|
||||
|
||||
type clientStateAlloc struct {
|
||||
|
||||
@@ -38,10 +38,16 @@ func TestOperatorClientStateCommand(t *testing.T) {
|
||||
alloc := structs.MockAlloc()
|
||||
err = db.PutAllocation(alloc)
|
||||
must.NoError(t, err)
|
||||
|
||||
// Write a node identity to the DB, so we can test that the command reads
|
||||
// this data.
|
||||
must.NoError(t, db.PutNodeIdentity("mynodeidentity"))
|
||||
|
||||
must.NoError(t, db.Close())
|
||||
|
||||
// run against an incomplete client state directory
|
||||
code = cmd.Run([]string{dir})
|
||||
must.Eq(t, 0, code)
|
||||
must.StrContains(t, ui.OutputWriter.String(), alloc.ID)
|
||||
must.StrContains(t, ui.OutputWriter.String(), "NodeIdentity\":\"mynodeidentity")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user