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 }