github.com/go-chef/chef@v0.30.1/node.go (about)

     1  package chef
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  )
     7  
     8  var (
     9  	ErrPathNotFound   = errors.New("attribute path not found")
    10  	ErrNoPathProvided = errors.New("no path was provided")
    11  )
    12  
    13  type NodeService struct {
    14  	client *Client
    15  }
    16  
    17  // Node represents the native Go version of the deserialized Node type
    18  type Node struct {
    19  	Name                string                 `json:"name"`
    20  	Environment         string                 `json:"chef_environment,omitempty"`
    21  	ChefType            string                 `json:"chef_type,omitempty"`
    22  	AutomaticAttributes map[string]interface{} `json:"automatic,omitempty"`
    23  	NormalAttributes    map[string]interface{} `json:"normal,omitempty"`
    24  	DefaultAttributes   map[string]interface{} `json:"default,omitempty"`
    25  	OverrideAttributes  map[string]interface{} `json:"override,omitempty"`
    26  	JsonClass           string                 `json:"json_class,omitempty"`
    27  	//TODO: use the RunList struct for this
    28  	RunList     []string `json:"run_list,omitempty"`
    29  	PolicyName  string   `json:"policy_name,omitempty"`
    30  	PolicyGroup string   `json:"policy_group,omitempty"`
    31  }
    32  
    33  // GetAttribute will fetch an attribute from the provided path considering the right attribute precedence.
    34  func (e *Node) GetAttribute(paths ...string) (interface{}, error) {
    35  	if len(paths) <= 0 {
    36  		return nil, ErrNoPathProvided
    37  	}
    38  
    39  	// this follows the Chef attribute precedence: https://docs.chef.io/attribute_precedence/
    40  	attrList := []map[string]interface{}{e.AutomaticAttributes, e.OverrideAttributes, e.NormalAttributes, e.DefaultAttributes}
    41  
    42  	for _, attrs := range attrList {
    43  		attr, err := lookupAttribute(attrs, paths...)
    44  		if err != nil {
    45  			if errors.Is(err, ErrPathNotFound) {
    46  				continue
    47  			}
    48  
    49  			return nil, err
    50  		}
    51  
    52  		return attr, nil
    53  	}
    54  
    55  	return nil, ErrPathNotFound
    56  }
    57  
    58  // looks up a complete path in the provided attribute map.
    59  func lookupAttribute(attrs map[string]interface{}, paths ...string) (interface{}, error) {
    60  	if len(paths) <= 0 {
    61  		return nil, ErrPathNotFound
    62  	}
    63  
    64  	currentPath, remainingPaths := paths[0], paths[1:]
    65  
    66  	if attr, ok := attrs[currentPath]; ok {
    67  		if len(remainingPaths) <= 0 {
    68  			return attr, nil // we are at the last provided part of the path
    69  		}
    70  
    71  		// otherwise keep looking until we reach the end
    72  		return lookupAttribute(attr.(map[string]interface{}), remainingPaths...)
    73  	}
    74  
    75  	return nil, ErrPathNotFound
    76  }
    77  
    78  type NodeResult struct {
    79  	Uri string `json:"uri"`
    80  }
    81  
    82  // NewNode is the Node constructor method
    83  func NewNode(name string) (node Node) {
    84  	node = Node{
    85  		Name:        name,
    86  		Environment: "_default",
    87  		ChefType:    "node",
    88  		JsonClass:   "Chef::Node",
    89  	}
    90  	return
    91  }
    92  
    93  // List lists the nodes in the Chef server.
    94  //
    95  // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes
    96  func (e *NodeService) List() (data map[string]string, err error) {
    97  	err = e.client.magicRequestDecoder("GET", "nodes", nil, &data)
    98  	return
    99  }
   100  
   101  // Get gets a node from the Chef server.
   102  //
   103  // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes-name
   104  func (e *NodeService) Get(name string) (node Node, err error) {
   105  	url := fmt.Sprintf("nodes/%s", name)
   106  	err = e.client.magicRequestDecoder("GET", url, nil, &node)
   107  	return
   108  }
   109  
   110  // Head gets a node from the Chef server. Does not return a json body.
   111  //
   112  // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes-name
   113  func (e *NodeService) Head(name string) (err error) {
   114  	url := fmt.Sprintf("nodes/%s", name)
   115  	err = e.client.magicRequestDecoder("HEAD", url, nil, nil)
   116  	return
   117  }
   118  
   119  // Post creates a Node on the chef server
   120  //
   121  // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes
   122  func (e *NodeService) Post(node Node) (data *NodeResult, err error) {
   123  	body, err := JSONReader(node)
   124  	if err != nil {
   125  		return
   126  	}
   127  
   128  	err = e.client.magicRequestDecoder("POST", "nodes", body, &data)
   129  	return
   130  }
   131  
   132  // Put updates a node on the Chef server.
   133  //
   134  // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes-name
   135  // TODO: We might want to change the name. name and data should be separate structures
   136  func (e *NodeService) Put(n Node) (node Node, err error) {
   137  	url := fmt.Sprintf("nodes/%s", n.Name)
   138  	body, err := JSONReader(n)
   139  	if err != nil {
   140  		return
   141  	}
   142  
   143  	err = e.client.magicRequestDecoder("PUT", url, body, &node)
   144  	return
   145  }
   146  
   147  // Delete removes a node on the Chef server
   148  //
   149  // Chef API docs: https://docs.chef.io/api_chef_server.html#nodes-name
   150  func (e *NodeService) Delete(name string) (err error) {
   151  	err = e.client.magicRequestDecoder("DELETE", "nodes/"+name, nil, nil)
   152  	return
   153  }