From a22892f4bf6a1eba26a48e962cc38cc7ec3a06ae Mon Sep 17 00:00:00 2001
From: Massimiliano Adamo <massimiliano.adamo@geant.org>
Date: Thu, 3 Jun 2021 22:13:56 +0200
Subject: [PATCH] add update function

---
 go.mod  |  1 +
 go.sum  |  2 ++
 main.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 66 insertions(+), 7 deletions(-)

diff --git a/go.mod b/go.mod
index 099344e..4aa04c1 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.16
 require (
 	github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
 	github.com/go-ini/ini v1.62.0
+	github.com/hashicorp/go-version v1.3.0
 	github.com/smartystreets/goconvey v1.6.4 // indirect
 	github.com/tidwall/gjson v1.8.0
 	gopkg.in/ini.v1 v1.62.0 // indirect
diff --git a/go.sum b/go.sum
index 5209733..f973110 100644
--- a/go.sum
+++ b/go.sum
@@ -4,6 +4,8 @@ github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU=
 github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
+github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
diff --git a/main.go b/main.go
index a4a8351..47bb5fc 100644
--- a/main.go
+++ b/main.go
@@ -3,6 +3,7 @@ package main
 import (
 	"crypto/tls"
 	"crypto/x509"
+	"encoding/json"
 	"encoding/pem"
 	"fmt"
 	"io"
@@ -18,12 +19,22 @@ import (
 
 	"github.com/docopt/docopt-go"
 	"github.com/go-ini/ini"
+	"github.com/hashicorp/go-version"
 	"github.com/tidwall/gjson"
 )
 
 const errMsg string = "[ERR]"
 const infoMsg string = "[INFO]"
 
+type Properties struct {
+	Version []string `json:"version"`
+}
+
+type JsonArtifact struct {
+	Properties Properties
+	Uri        string
+}
+
 var (
 	appVersion                string
 	buildTime                 string
@@ -49,6 +60,7 @@ var (
 	certTmpDir                string
 	key                       *x509.Certificate
 	cert                      *x509.Certificate
+	httpClient                = &http.Client{Timeout: 10 * time.Second}
 )
 
 // app clean and exit
@@ -65,6 +77,22 @@ func appExit(status int) {
 	os.Exit(status)
 }
 
+// get upstream version
+func getUpstreamVersion(url string) string {
+	resp, err := httpClient.Get(url)
+	if err != nil {
+		fmt.Printf("Request to Artifactory failed: %v\n", err)
+		appExit(255)
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		fmt.Printf("Reading body from Artifactory failed: %v\n", err)
+		appExit(255)
+	}
+	return string(body)
+}
+
 // check certificates
 func checkCertificates(dnsname string, certificate string, fullchain string, ca string, key string, days int, fail bool) bool {
 
@@ -331,6 +359,7 @@ func downloadFile(filepath string, url string) (err error) {
 
 func main() {
 
+	progName := filepath.Base(os.Args[0])
 	OSInfo := ReadOSRelease("/etc/os-release")
 	OSRelease := OSInfo["ID"]
 	if OSRelease == "centos" || OSRelease == "rhel" {
@@ -361,11 +390,12 @@ func main() {
   - fetches and stores a given Certificate, Full Chain, CA and Private Key
 
 Usage:
-  acme-downloader --redis-token=REDISTOKEN --vault-token=VAULTTOKEN --cert-name=CERTNAME --team-name=TEAMNAME [--silent] [--days=DAYS] [--type=TYPE] [--cert-destination=CERTDESTINATION] [--fullchain-destination=FULLCHAINDESTINATION] [--key-destination=KEYDESTINATION] [--ca-destination=CADESTINATION] [--wildcard]
-  acme-downloader -h | --help
-  acme-downloader -v | --version
-  acme-downloader -b | --build
-  acme-downloader --update
+  %v --redis-token=REDISTOKEN --vault-token=VAULTTOKEN --cert-name=CERTNAME --team-name=TEAMNAME [--silent] [--days=DAYS] [--type=TYPE] [--cert-destination=CERTDESTINATION] [--fullchain-destination=FULLCHAINDESTINATION] [--key-destination=KEYDESTINATION] [--ca-destination=CADESTINATION] [--wildcard]
+  %v -h | --help
+  %v -v | --version
+  %v -b | --build
+  %v --update
+  %v --check-version
 
 Options:
   -h --help                                     Show this screen
@@ -383,15 +413,41 @@ Options:
   --ca-destination=CADESTINATION                CA Destination [default: %v/COMODO_<type>.crt]
   --wildcard                                    The certificate type is wildcard
   --update                                      Self-updates the tool and exit
-`, CertBase, CertBase, KeyBase, CertBase)
+  --check-version                               Check upstream version
+`, progName, progName, progName, progName, progName, progName, CertBase, CertBase, KeyBase, CertBase)
 
 	arguments, _ := docopt.Parse(usage, nil, true, appVersion, false)
 
 	if arguments["--build"] == true {
-		fmt.Printf("acme-downloader version: %v, built on: %v\n", appVersion, buildTime)
+		fmt.Printf("%v version: %v, built on: %v\n", progName, appVersion, buildTime)
+		appExit(0)
+	}
+
+	// chekc upstream version
+	metadataBase := "https://artifactory.software.geant.org/artifactory/api/storage/acme-downloader/acme-downloader"
+	metadataURL := fmt.Sprintf("%v_%v_%v?properties=version", metadataBase, runtime.GOOS, runtime.GOARCH)
+	if runtime.GOOS == "windows" {
+		metadataURL = fmt.Sprintf("%v_%v_%v.exe?properties=version", metadataBase, runtime.GOOS, runtime.GOARCH)
+	}
+	upstreamResp := getUpstreamVersion(metadataURL)
+	var jsonartifact JsonArtifact
+	json.Unmarshal([]byte(upstreamResp), &jsonartifact)
+	upstreamVersion := fmt.Sprintf(jsonartifact.Properties.Version[0])
+	localVersion, _ := version.NewVersion(appVersion)
+	remoteVersion, _ := version.NewVersion(upstreamVersion)
+	if localVersion.GreaterThan(remoteVersion) {
+		fmt.Printf("local version (%s) is older than upstream version (%s)\n", localVersion, remoteVersion)
+		fmt.Printf("please use --update to install the new version\n")
+	} else {
+		if arguments["--check-version"] == true {
+			fmt.Printf("You are running the latest version (%v)\n", appVersion)
+		}
+	}
+	if arguments["--check-version"] == true {
 		appExit(0)
 	}
 
+	// fetch and install the upstream version
 	if arguments["--update"] == true {
 		ArtifactoryBase := "https://artifactory.software.geant.org/artifactory/acme-downloader"
 		ArtifactName := fmt.Sprintf("acme-downloader_%v_%v", runtime.GOOS, runtime.GOARCH)
-- 
GitLab