From a37d00b40a9cf1d163abeb6b55e99397f39238ca Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 9 Nov 2021 17:07:06 +0200 Subject: [PATCH] checking latest version --- build.py | 2 +- cautils/getter/getpoliciesutils.go | 22 +++- cautils/versioncheck.go | 112 ++++++++++++++++++ clihandler/cmd/version.go | 33 +----- clihandler/initcli.go | 6 + main.go | 20 ---- .../reporter/reporteventreceiver.go | 16 +-- .../reporter/reporteventreceiverutils.go | 25 ---- 8 files changed, 147 insertions(+), 89 deletions(-) create mode 100644 cautils/versioncheck.go diff --git a/build.py b/build.py index 685604bb..ec8a5497 100644 --- a/build.py +++ b/build.py @@ -41,7 +41,7 @@ def main(): # Set some variables packageName = getPackageName() - buildUrl = "github.com/armosec/kubescape/clihandler/cmd.BuildNumber" + buildUrl = "github.com/armosec/kubescape/cautils.BuildNumber" releaseVersion = os.getenv("RELEASE") ArmoBEServer = os.getenv("ArmoBEServer") ArmoERServer = os.getenv("ArmoERServer") diff --git a/cautils/getter/getpoliciesutils.go b/cautils/getter/getpoliciesutils.go index 7949eb70..a8b4a271 100644 --- a/cautils/getter/getpoliciesutils.go +++ b/cautils/getter/getpoliciesutils.go @@ -1,6 +1,7 @@ package getter import ( + "bytes" "encoding/json" "fmt" "io" @@ -87,7 +88,24 @@ func HttpGetter(httpClient *http.Client, fullURL string) (string, error) { if err != nil { return "", err } - respStr, err := httpRespToString(resp) + respStr, err := HttpRespToString(resp) + if err != nil { + return "", err + } + return respStr, nil +} + +func HttpPost(httpClient *http.Client, fullURL string, body []byte) (string, error) { + + req, err := http.NewRequest("POST", fullURL, bytes.NewReader(body)) + if err != nil { + return "", err + } + resp, err := httpClient.Do(req) + if err != nil { + return "", err + } + respStr, err := HttpRespToString(resp) if err != nil { return "", err } @@ -95,7 +113,7 @@ func HttpGetter(httpClient *http.Client, fullURL string) (string, error) { } // HTTPRespToString parses the body as string and checks the HTTP status code, it closes the body reader at the end -func httpRespToString(resp *http.Response) (string, error) { +func HttpRespToString(resp *http.Response) (string, error) { if resp == nil || resp.Body == nil { return "", nil } diff --git a/cautils/versioncheck.go b/cautils/versioncheck.go new file mode 100644 index 00000000..c8bdcbc1 --- /dev/null +++ b/cautils/versioncheck.go @@ -0,0 +1,112 @@ +package cautils + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + + "github.com/armosec/kubescape/cautils/getter" + pkgutils "github.com/armosec/utils-go/utils" +) + +const SKIP_VERSION_CHECK = "KUBESCAPE_SKIP_UPDATE_CHECK" + +var BuildNumber string + +type IVersionCheckHandler interface { + CheckLatestVersion(*VersionCheckRequest) error +} + +func NewIVersionCheckHandler() IVersionCheckHandler { + if v, ok := os.LookupEnv(SKIP_VERSION_CHECK); ok && pkgutils.StringToBool(v) { + return NewVersionCheckHandlerMock() + } + return NewVersionCheckHandler() +} + +type VersionCheckHandlerMock struct { +} + +func NewVersionCheckHandlerMock() *VersionCheckHandlerMock { + return &VersionCheckHandlerMock{} +} + +type VersionCheckHandler struct { + versionURL string +} +type VersionCheckRequest struct { + Client string `json:"client"` // kubescape + ClientVersion string `json:"clientVersion"` // kubescape version + Framework string `json:"framework"` // framework name + FrameworkVersion string `json:"frameworkVersion"` // framework version + ScanningTarget string `json:"target"` // scanning target- cluster/yaml +} + +type VersionCheckResponse struct { + Client string `json:"client"` // kubescape + ClientUpdate string `json:"clientUpdate"` // kubescape latest version + Framework string `json:"framework"` // framework name + FrameworkUpdate string `json:"frameworkUpdate"` // framework latest version + Message string `json:"message"` // alert message +} + +func NewVersionCheckHandler() *VersionCheckHandler { + return &VersionCheckHandler{ + versionURL: "https://us-central1-elated-pottery-310110.cloudfunctions.net/ksgf1v1", + } +} +func NewVersionCheckRequest(buildNumber, frameworkName, frameworkVersion, scanningTarget string) *VersionCheckRequest { + return &VersionCheckRequest{ + Client: "kubescape", + ClientVersion: buildNumber, + Framework: frameworkName, + FrameworkVersion: frameworkVersion, + ScanningTarget: scanningTarget, + } +} + +func (v *VersionCheckHandlerMock) CheckLatestVersion(versionData *VersionCheckRequest) error { + fmt.Println("Skipping version check") + return nil +} + +func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckRequest) error { + + latestVersion, err := v.getLatestVersion(versionData) + if err != nil || latestVersion == nil { + return fmt.Errorf("failed to get latest version: %v", err) + } + + if latestVersion.ClientUpdate != "" { + fmt.Println(warningMessage(latestVersion.Client, latestVersion.ClientUpdate)) + } + + if latestVersion.FrameworkUpdate != "" { + fmt.Println(warningMessage(latestVersion.Framework, latestVersion.FrameworkUpdate)) + } + return nil +} + +func (v *VersionCheckHandler) getLatestVersion(versionData *VersionCheckRequest) (*VersionCheckResponse, error) { + + reqBody, err := json.Marshal(versionData) + if err != nil { + return nil, fmt.Errorf("in 'CheckLatestVersion' failed to json.Marshal, reason: %v", err) + } + + resp, err := getter.HttpPost(http.DefaultClient, v.versionURL, reqBody) + if err != nil { + return nil, err + } + + vResp := &VersionCheckResponse{} + if err = getter.JSONDecoder(resp).Decode(vResp); err != nil { + return nil, err + } + return vResp, nil +} + +func warningMessage(kind, release string) string { + return fmt.Sprintf("Warning: '%s' is not updated to the latest release: '%s'", kind, release) +} diff --git a/clihandler/cmd/version.go b/clihandler/cmd/version.go index f4623813..aa47b0cb 100644 --- a/clihandler/cmd/version.go +++ b/clihandler/cmd/version.go @@ -1,49 +1,24 @@ package cmd import ( - "encoding/json" "fmt" - "io" - "net/http" + "github.com/armosec/kubescape/cautils" "github.com/spf13/cobra" ) -var BuildNumber string - var versionCmd = &cobra.Command{ Use: "version", Short: "Get current version", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { - fmt.Println("Your current version is: " + BuildNumber) + v := cautils.NewIVersionCheckHandler() + v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, "", "", "")) + fmt.Println("Your current version is: " + cautils.BuildNumber) return nil }, } -func GetLatestVersion() (string, error) { - latestVersion := "https://api.github.com/repos/armosec/kubescape/releases/latest" - resp, err := http.Get(latestVersion) - if err != nil { - return "unknown", fmt.Errorf("failed to get latest releases from '%s', reason: %s", latestVersion, err.Error()) - } - defer resp.Body.Close() - if resp.StatusCode < 200 || 301 < resp.StatusCode { - return "unknown", nil - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return "unknown", fmt.Errorf("failed to read response body from '%s', reason: %s", latestVersion, err.Error()) - } - var data map[string]interface{} - err = json.Unmarshal(body, &data) - if err != nil { - return "unknown", fmt.Errorf("failed to unmarshal response body from '%s', reason: %s", latestVersion, err.Error()) - } - return fmt.Sprintf("%v", data["tag_name"]), nil -} - func init() { rootCmd.AddCommand(versionCmd) } diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 153eeeb1..bcea8aa3 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -54,6 +54,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { var resourceHandler resourcehandler.IResourceHandler var clusterConfig cautils.IClusterConfig var reportHandler reporter.IReport + var scanningTarget string if !scanInfo.ScanRunningCluster() { k8sinterface.ConnectedToCluster = false @@ -64,6 +65,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { // set mock report (do not send report) reportHandler = reporter.NewReportMock() + scanningTarget = "yaml" } else { k8s := k8sinterface.NewKubernetesApi() resourceHandler = resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo)) @@ -71,8 +73,12 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { // setup reporter reportHandler = getReporter(scanInfo) + scanningTarget = "cluster" } + v := cautils.NewIVersionCheckHandler() + v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, "", "", scanningTarget)) + // setup printer printerHandler := printer.GetPrinter(scanInfo.Format) printerHandler.SetWriter(scanInfo.Output) diff --git a/main.go b/main.go index 84329f92..07bf089e 100644 --- a/main.go +++ b/main.go @@ -1,29 +1,9 @@ package main import ( - "fmt" - "os" - "github.com/armosec/kubescape/clihandler/cmd" - pkgutils "github.com/armosec/utils-go/utils" ) -const SKIP_VERSION_CHECK = "KUBESCAPE_SKIP_UPDATE_CHECK" - func main() { - CheckLatestVersion() cmd.Execute() } - -func CheckLatestVersion() { - if v, ok := os.LookupEnv(SKIP_VERSION_CHECK); ok && pkgutils.StringToBool(v) { - return - } - latest, err := cmd.GetLatestVersion() - if err != nil || latest == "unknown" { - return - } - if latest != cmd.BuildNumber { - fmt.Println("Warning: You are not updated to the latest release: " + latest) - } -} diff --git a/resultshandling/reporter/reporteventreceiver.go b/resultshandling/reporter/reporteventreceiver.go index 6ce5a08d..83e4d033 100644 --- a/resultshandling/reporter/reporteventreceiver.go +++ b/resultshandling/reporter/reporteventreceiver.go @@ -1,12 +1,12 @@ package reporter import ( - "bytes" "encoding/json" "fmt" "net/http" "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/opa-utils/reporthandling" ) @@ -17,14 +17,14 @@ type IReport interface { } type ReportEventReceiver struct { - httpClient http.Client + httpClient *http.Client clusterName string customerGUID string } func NewReportEventReceiver(customerGUID, clusterName string) *ReportEventReceiver { return &ReportEventReceiver{ - httpClient: http.Client{}, + httpClient: &http.Client{}, clusterName: clusterName, customerGUID: customerGUID, } @@ -58,15 +58,7 @@ func (report *ReportEventReceiver) send(postureReport *reporthandling.PostureRep } host := hostToString(report.initEventReceiverURL(), postureReport.ReportID) - req, err := http.NewRequest("POST", host, bytes.NewReader(reqBody)) - if err != nil { - return fmt.Errorf("in 'Send', http.NewRequest failed, host: %s, reason: %v", host, err) - } - res, err := report.httpClient.Do(req) - if err != nil { - return fmt.Errorf("httpClient.Do failed: %v", err) - } - msg, err := httpRespToString(res) + msg, err := getter.HttpPost(report.httpClient, host, reqBody) if err != nil { return fmt.Errorf("%s, %v:%s", host, err, msg) } diff --git a/resultshandling/reporter/reporteventreceiverutils.go b/resultshandling/reporter/reporteventreceiverutils.go index a32a3c22..0f90dbae 100644 --- a/resultshandling/reporter/reporteventreceiverutils.go +++ b/resultshandling/reporter/reporteventreceiverutils.go @@ -1,37 +1,12 @@ package reporter import ( - "fmt" - "io" - "net/http" "net/url" - "strings" "github.com/armosec/kubescape/cautils/getter" "github.com/gofrs/uuid" ) -// HTTPRespToString parses the body as string and checks the HTTP status code, it closes the body reader at the end -func httpRespToString(resp *http.Response) (string, error) { - if resp == nil || resp.Body == nil { - return "", nil - } - strBuilder := strings.Builder{} - defer resp.Body.Close() - if resp.ContentLength > 0 { - strBuilder.Grow(int(resp.ContentLength)) - } - _, err := io.Copy(&strBuilder, resp.Body) - if err != nil { - return strBuilder.String(), err - } - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - err = fmt.Errorf("response status: %d. Content: %s", resp.StatusCode, strBuilder.String()) - } - - return strBuilder.String(), err -} - func (report *ReportEventReceiver) initEventReceiverURL() *url.URL { urlObj := url.URL{}