github.com/jlmeeker/kismatic@v1.10.1-0.20180612190640-57f9005a1f1a/pkg/inspector/client.go (about)

     1  package inspector
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  
    10  	"github.com/apprenda/kismatic/pkg/inspector/rule"
    11  )
    12  
    13  // The Client executes rules against a remote inspector
    14  type Client struct {
    15  	// TargetNode is the ip:port of the inspector running on the remote node
    16  	TargetNode string
    17  	// TargetNodeRole is the role of the node we are inspecting
    18  	TargetNodeFacts []string
    19  	engine          *rule.Engine
    20  }
    21  
    22  // NewClient returns an inspector client for running checks against remote nodes.
    23  func NewClient(targetNode string, targetNodeFacts []string) (*Client, error) {
    24  	host, _, err := net.SplitHostPort(targetNode)
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	engine := &rule.Engine{
    29  		RuleCheckMapper: rule.DefaultCheckMapper{
    30  			PackageManager: nil, // Use a no-op pkg manager here instead
    31  			TargetNodeIP:   host,
    32  		},
    33  	}
    34  	return &Client{
    35  		TargetNode:      targetNode,
    36  		TargetNodeFacts: targetNodeFacts,
    37  		engine:          engine,
    38  	}, nil
    39  }
    40  
    41  // ExecuteRules against the target inspector server
    42  func (c Client) ExecuteRules(rules []rule.Rule) ([]rule.Result, error) {
    43  	serverSideRules := getServerSideRules(rules)
    44  	d, err := json.Marshal(serverSideRules)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("error marshaling check request: %v", err)
    47  	}
    48  	resp, err := http.Post(fmt.Sprintf("http://%s%s", c.TargetNode, executeEndpoint), "application/json", bytes.NewReader(d))
    49  	if err != nil {
    50  		return nil, fmt.Errorf("error posting request to server: %v", err)
    51  	}
    52  	defer resp.Body.Close()
    53  	// verify response status code
    54  	if resp.StatusCode == http.StatusInternalServerError {
    55  		errMsg := &serverError{}
    56  		if err = json.NewDecoder(resp.Body).Decode(errMsg); err != nil {
    57  			return nil, fmt.Errorf("failed to decode server response: %v. Server sent %q status", err, resp.Status)
    58  		}
    59  		return nil, fmt.Errorf("server sent %q status: error from server: %s", http.StatusInternalServerError, errMsg.Error)
    60  	}
    61  	if resp.StatusCode != http.StatusOK {
    62  		return nil, fmt.Errorf("server responded with non-successful status: %q", resp.Status)
    63  	}
    64  
    65  	// we got an OK - handle the response
    66  	results := []rule.Result{}
    67  	err = json.NewDecoder(resp.Body).Decode(&results)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("error decoding server response: %v", err)
    70  	}
    71  
    72  	// Execute the rules that should run from a remote node
    73  	clientSideRules := getClientSideRules(rules)
    74  	remoteResults, err := c.engine.ExecuteRules(clientSideRules, c.TargetNodeFacts)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	results = append(results, remoteResults...)
    79  
    80  	endpoint := fmt.Sprintf("http://%s%s", c.TargetNode, closeEndpoint)
    81  	resp, err = http.Get(endpoint)
    82  	if err != nil {
    83  		return nil, fmt.Errorf("GET request to %q failed. You might have to restart the inspector server. Error was: %v", endpoint, err)
    84  	}
    85  
    86  	return results, nil
    87  }
    88  
    89  func getServerSideRules(rules []rule.Rule) []rule.Rule {
    90  	localRules := []rule.Rule{}
    91  	for _, r := range rules {
    92  		if !r.IsRemoteRule() {
    93  			localRules = append(localRules, r)
    94  		}
    95  	}
    96  	return localRules
    97  }
    98  
    99  func getClientSideRules(rules []rule.Rule) []rule.Rule {
   100  	remoteRules := []rule.Rule{}
   101  	for _, r := range rules {
   102  		if r.IsRemoteRule() {
   103  			remoteRules = append(remoteRules, r)
   104  		}
   105  	}
   106  	return remoteRules
   107  }