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

     1  package inspector
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"net/http"
     9  
    10  	"github.com/apprenda/kismatic/pkg/inspector/check"
    11  	"github.com/apprenda/kismatic/pkg/inspector/rule"
    12  )
    13  
    14  // Server supports the execution of inspector rules from a remote node
    15  type Server struct {
    16  	// The Port the server will listen on
    17  	Port int
    18  	// NodeFacts are the facts that apply to the node where the server is running
    19  	NodeFacts []string
    20  	// RulesEngine for running inspector rules
    21  	rulesEngine *rule.Engine
    22  }
    23  
    24  type serverError struct {
    25  	Error string
    26  }
    27  
    28  var executeEndpoint = "/execute"
    29  var closeEndpoint = "/close"
    30  
    31  // NewServer returns an inspector server that has been initialized
    32  // with the default rules engine
    33  func NewServer(nodeFacts []string, port int, packageInstallationDisabled bool, dockerInstallationDisabled bool, disconnectedInstallation bool) (*Server, error) {
    34  	s := &Server{
    35  		Port: port,
    36  	}
    37  	distro, err := check.DetectDistro()
    38  	if err != nil {
    39  		return nil, fmt.Errorf("error building server: %v", err)
    40  	}
    41  	s.NodeFacts = append(nodeFacts, string(distro))
    42  	pkgMgr, err := check.NewPackageManager(distro)
    43  	if err != nil {
    44  		return nil, fmt.Errorf("error building server: %v", err)
    45  	}
    46  	engine := &rule.Engine{
    47  		RuleCheckMapper: rule.DefaultCheckMapper{
    48  			PackageManager:              pkgMgr,
    49  			PackageInstallationDisabled: packageInstallationDisabled,
    50  			DockerInstallationDisabled:  dockerInstallationDisabled,
    51  			DisconnectedInstallation:    disconnectedInstallation,
    52  		},
    53  	}
    54  	s.rulesEngine = engine
    55  	return s, nil
    56  }
    57  
    58  // Start the server
    59  func (s *Server) Start() error {
    60  	mux := http.NewServeMux()
    61  	// Execute endpoint
    62  	mux.HandleFunc(executeEndpoint, func(w http.ResponseWriter, req *http.Request) {
    63  		if req.Method != http.MethodPost {
    64  			w.WriteHeader(http.StatusMethodNotAllowed)
    65  			return
    66  		}
    67  		// Decode rules
    68  		data, err := ioutil.ReadAll(req.Body)
    69  		if err != nil {
    70  			w.WriteHeader(http.StatusInternalServerError)
    71  			log.Printf("error decoding rules when processing request: %v", err)
    72  			return
    73  		}
    74  		defer req.Body.Close()
    75  		rules, err := rule.UnmarshalRulesJSON(data)
    76  		if err != nil {
    77  			w.WriteHeader(http.StatusBadRequest)
    78  			log.Printf("error unmarshaling rules from JSON: %v", err)
    79  			return
    80  		}
    81  		// Run the rules that we received
    82  		results, err := s.rulesEngine.ExecuteRules(rules, s.NodeFacts)
    83  		if err != nil {
    84  			err = json.NewEncoder(w).Encode(serverError{Error: err.Error()})
    85  			if err != nil {
    86  				log.Printf("error writing server response: %v\n", err)
    87  			}
    88  			w.WriteHeader(http.StatusInternalServerError)
    89  			return
    90  		}
    91  		err = json.NewEncoder(w).Encode(results)
    92  		if err != nil {
    93  			log.Printf("error writing server response: %v\n", err)
    94  			w.WriteHeader(http.StatusInternalServerError)
    95  		}
    96  	})
    97  	// Close endpoint
    98  	mux.HandleFunc(closeEndpoint, func(w http.ResponseWriter, req *http.Request) {
    99  		err := s.rulesEngine.CloseChecks()
   100  		if err != nil {
   101  			log.Printf("error closing checks: %v", err)
   102  			w.WriteHeader(http.StatusInternalServerError)
   103  		}
   104  		w.WriteHeader(http.StatusOK)
   105  	})
   106  	return http.ListenAndServe(fmt.Sprintf(":%d", s.Port), mux)
   107  }