github.com/RedHatInsights/insights-content-service@v1.0.0/content-service.go (about)

     1  /*
     2  Copyright © 2020, 2021 Red Hat, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Entry point to the insights content service
    18  package main
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/json"
    23  	"fmt"
    24  	"os"
    25  	"strings"
    26  
    27  	"github.com/RedHatInsights/insights-operator-utils/logger"
    28  	"github.com/RedHatInsights/insights-operator-utils/metrics"
    29  	"github.com/rs/zerolog/log"
    30  
    31  	"github.com/RedHatInsights/insights-content-service/conf"
    32  	"github.com/RedHatInsights/insights-content-service/content"
    33  	"github.com/RedHatInsights/insights-content-service/groups"
    34  	"github.com/RedHatInsights/insights-content-service/server"
    35  )
    36  
    37  // ExitCode represents numeric value returned to parent process when the
    38  // current process finishes
    39  type ExitCode int
    40  
    41  const (
    42  	// ExitStatusOK means that the tool finished with success
    43  	ExitStatusOK = iota
    44  
    45  	// ExitStatusServerError is returned in case of any REST API server-related error
    46  	ExitStatusServerError
    47  
    48  	// ExitStatusReadContentError is returned when the static content parsing fails
    49  	ExitStatusReadContentError
    50  
    51  	// ExitStatusOther represents other errors that might happen
    52  	ExitStatusOther
    53  
    54  	defaultConfigFilename = "config"
    55  )
    56  
    57  var (
    58  	serverInstance *server.HTTPServer
    59  
    60  	// BuildVersion contains the major.minor version of the CLI client
    61  	BuildVersion = "*not set*"
    62  
    63  	// BuildTime contains timestamp when the CLI client has been built
    64  	BuildTime = "*not set*"
    65  
    66  	// BuildBranch contains Git branch used to build this application
    67  	BuildBranch = "*not set*"
    68  
    69  	// BuildCommit contains Git commit used to build this application
    70  	BuildCommit = "*not set*"
    71  
    72  	// UtilsVersion contains currently used version of
    73  	// github.com/RedHatInsights/insights-operator-utils package
    74  	UtilsVersion = "*not set*"
    75  
    76  	// OCPRulesVersion contains currently used version of
    77  	// https://gitlab.cee.redhat.com/ccx/ccx-rules-ocp package
    78  	OCPRulesVersion = "*not set*"
    79  )
    80  
    81  // startService starts service and returns error code
    82  func startService() ExitCode {
    83  	serverCfg := conf.GetServerConfiguration()
    84  	groupsCfg := conf.GetGroupsConfiguration()
    85  
    86  	parsedGroups, err := groups.ParseGroupConfigFile(groupsCfg.ConfigPath)
    87  	if err != nil {
    88  		log.Error().Err(err).Msg("Groups init error")
    89  		return ExitStatusServerError
    90  	}
    91  
    92  	metricsCfg := conf.GetMetricsConfiguration()
    93  	if metricsCfg.Namespace != "" {
    94  		metrics.AddAPIMetricsWithNamespace(metricsCfg.Namespace)
    95  	}
    96  
    97  	ruleContentDirPath := conf.GetContentPathConfiguration()
    98  
    99  	contentDir, ruleContentStatusMap, err := content.ParseRuleContentDir(ruleContentDirPath)
   100  	if osPathError, ok := err.(*os.PathError); ok {
   101  		log.Error().Err(osPathError).Msg("No rules directory")
   102  		return ExitStatusReadContentError
   103  	} else if err != nil {
   104  		log.Error().Err(err).Msg("error happened during parsing rules content dir")
   105  		return ExitStatusReadContentError
   106  	}
   107  
   108  	// start the HTTP server on specified port
   109  	serverInstance = server.New(serverCfg, parsedGroups, contentDir,
   110  		ruleContentStatusMap)
   111  
   112  	// fill-in additional info used by /info endpoint handler
   113  	fillInInfoParams(serverInstance.InfoParams)
   114  
   115  	err = serverInstance.Start()
   116  	if err != nil {
   117  		log.Error().Err(err).Msg("HTTP(s) start error")
   118  		return ExitStatusServerError
   119  	}
   120  
   121  	return ExitStatusOK
   122  }
   123  
   124  // fillInInfoParams function fills-in additional info used by /info endpoint
   125  // handler
   126  func fillInInfoParams(params map[string]string) {
   127  	params["BuildVersion"] = BuildVersion
   128  	params["BuildTime"] = BuildTime
   129  	params["BuildBranch"] = BuildBranch
   130  	params["BuildCommit"] = BuildCommit
   131  	params["UtilsVersion"] = UtilsVersion
   132  	params["OCPRulesVersion"] = OCPRulesVersion
   133  }
   134  
   135  func printInfo(msg, val string) {
   136  	fmt.Printf("%s\t%s\n", msg, val)
   137  }
   138  
   139  func printVersionInfo() ExitCode {
   140  	printInfo("Version:", BuildVersion)
   141  	printInfo("Build time:", BuildTime)
   142  	printInfo("Branch:", BuildBranch)
   143  	printInfo("Commit:", BuildCommit)
   144  	printInfo("Utils version:", UtilsVersion)
   145  	printInfo("OCP rules version:", OCPRulesVersion)
   146  	return ExitStatusOK
   147  }
   148  
   149  func printGroups() ExitCode {
   150  	groupsConfig := conf.GetGroupsConfiguration()
   151  	groupsMap, err := groups.ParseGroupConfigFile(groupsConfig.ConfigPath)
   152  
   153  	if err != nil {
   154  		log.Error().Err(err).Msg("Groups parsing error")
   155  		return ExitStatusServerError
   156  	}
   157  
   158  	fmt.Println(groupsMap)
   159  	return ExitStatusOK
   160  }
   161  
   162  func printRules() ExitCode {
   163  	log.Info().Msg("Printing rules")
   164  	contentPath := conf.GetContentPathConfiguration()
   165  	contentDir, _, err := content.ParseRuleContentDir(contentPath)
   166  
   167  	if err != nil {
   168  		log.Error().Err(err).Msg("Error parsing the content")
   169  		return ExitStatusReadContentError
   170  	}
   171  
   172  	buffer := new(bytes.Buffer)
   173  	encoder := json.NewEncoder(buffer)
   174  
   175  	if err := encoder.Encode(contentDir); err == nil {
   176  		fmt.Println(buffer)
   177  		return ExitStatusOK
   178  	}
   179  
   180  	return ExitStatusOther
   181  
   182  }
   183  
   184  func printParseStatus() ExitCode {
   185  	log.Info().Msg("Printing parse status")
   186  	contentPath := conf.GetContentPathConfiguration()
   187  	_, parseStatus, err := content.ParseRuleContentDir(contentPath)
   188  
   189  	if err != nil {
   190  		log.Error().Err(err).Msg("Error parsing the content")
   191  		return ExitStatusReadContentError
   192  	}
   193  
   194  	buffer := new(bytes.Buffer)
   195  	encoder := json.NewEncoder(buffer)
   196  
   197  	if err := encoder.Encode(parseStatus); err == nil {
   198  		fmt.Println(buffer)
   199  		return ExitStatusOK
   200  	}
   201  
   202  	return ExitStatusOther
   203  
   204  }
   205  
   206  func initInfoLog(msg string) {
   207  	log.Info().Str("type", "init").Msg(msg)
   208  }
   209  
   210  func logVersionInfo() {
   211  	initInfoLog("Version: " + BuildVersion)
   212  	initInfoLog("Build time: " + BuildTime)
   213  	initInfoLog("Branch: " + BuildBranch)
   214  	initInfoLog("Commit: " + BuildCommit)
   215  	initInfoLog("Utils version:" + UtilsVersion)
   216  	initInfoLog("OCP rules version:" + OCPRulesVersion)
   217  }
   218  
   219  const helpMessageTemplate = `
   220  Service to provide content for OCP rules
   221  
   222  Usage:
   223  
   224      %+v [command]
   225  
   226  The commands are:
   227  
   228      <EMPTY>             starts content service
   229      start-service       starts content service
   230      help                prints help
   231      print-help          prints help
   232      print-config        prints current configuration set by files & env variables
   233      print-groups        prints current groups configuration
   234      print-rules         prints current parsed rules
   235      print-parse-status  prints information about all rules that have been parsed
   236      print-version-info  prints version info
   237  
   238  `
   239  
   240  func printHelp() ExitCode {
   241  	fmt.Printf(helpMessageTemplate, os.Args[0])
   242  	return ExitStatusOK
   243  }
   244  
   245  func printConfig(config conf.ConfigStruct) ExitCode {
   246  	configBytes, err := json.MarshalIndent(config, "", "    ")
   247  
   248  	if err != nil {
   249  		log.Error().Err(err)
   250  		return ExitStatusOther
   251  	}
   252  
   253  	fmt.Println(string(configBytes))
   254  
   255  	return ExitStatusOK
   256  }
   257  
   258  func main() {
   259  	err := conf.LoadConfiguration(defaultConfigFilename)
   260  	if err != nil {
   261  		panic(err)
   262  	}
   263  
   264  	err = logger.InitZerolog(
   265  		conf.GetLoggingConfiguration(),
   266  		conf.GetCloudWatchConfiguration(),
   267  		conf.GetSentryLoggingConfiguration(),
   268  		conf.GetKafkaZerologConfiguration(),
   269  	)
   270  	if err != nil {
   271  		panic(err)
   272  	}
   273  
   274  	command := "start-service"
   275  
   276  	if len(os.Args) >= 2 {
   277  		command = strings.ToLower(strings.TrimSpace(os.Args[1]))
   278  	}
   279  
   280  	os.Exit(int(handleCommand(command)))
   281  }
   282  
   283  func handleCommand(command string) ExitCode {
   284  	switch command {
   285  	case "start-service":
   286  		logVersionInfo()
   287  
   288  		errCode := startService()
   289  		if errCode != ExitStatusOK {
   290  			return errCode
   291  		}
   292  		return ExitStatusOK
   293  	case "help", "print-help":
   294  		return printHelp()
   295  	case "print-config":
   296  		return printConfig(conf.Config)
   297  	case "print-version-info":
   298  		return printVersionInfo()
   299  	case "print-groups":
   300  		return printGroups()
   301  	case "print-rules":
   302  		return printRules()
   303  	case "print-parse-status":
   304  		return printParseStatus()
   305  	default:
   306  		fmt.Printf("\nCommand '%v' not found\n", command)
   307  		return printHelp()
   308  	}
   309  }