diff --git a/go.mod b/go.mod index 099344e29db9f49ec0d63aba81ebd535dc8b3d78..4aa04c133011bd857100ca2f69166dcbb9d68c59 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 52097330124b9fd65896d73c223aba6b81737193..f97311072d0c790541581c1845d4a2d5525567e6 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 a4a8351287f7c35824d8e968250763c9d0792ba2..47bb5fcaa851376a6a55b72fd132a097bf636b53 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)