<commit_before>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package pct

import (
	"crypto"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

// todo: use real key, this is the test key
var PublicKey = []byte(`-----BEGIN PUBLIC KEY-----
<KEY>
-----END PUBLIC KEY-----`)

type Updater struct {
	logger         *Logger
	api            APIConnector
	currentBin     string
	currentVersion string
	// --
	rsaPubKey *rsa.PublicKey
	major     int64
	minor     int64
	patch     int64
}

func NewUpdater(logger *Logger, api APIConnector, pubKey []byte, currentBin, currentVersion string) *Updater {
	pubkeyMarshaled, _ := pem.Decode(pubKey)
	pubKeyParsed, err := x509.ParsePKIXPublicKey(pubkeyMarshaled.Bytes)
	if err != nil {
		panic(err)
	}
	rsaPubKey := pubKeyParsed.(*rsa.PublicKey)

	major, minor, patch := VersionStringToInts(currentVersion)
	u := &Updater{
		logger:         logger,
		api:            api,
		currentBin:     currentBin, // filepath.Abs(os.Args[0])
		currentVersion: currentVersion,
		// --
		rsaPubKey: rsaPubKey,
		major:     major,
		minor:     minor,
		patch:     patch,
	}
	return u
}

func (u *Updater) Check() (string, string, error) {
	url := fmt.Sprintf("%s/latest", u.api.EntryLink("download"), u.major, u.minor)
	v, err := u.download(url)
	if err != nil {
		return "", "", err
	}
	version := string(v)
	major, minor, patch := VersionStringToInts(version)
	switch {
	case major > u.major:
		return "major", version, nil
	case minor > u.minor:
		return "minor", version, nil
	case patch > u.patch:
		return "patch", version, nil
	default:
		return "", "", nil
	}
}

func (u *Updater) Update(version string) error {
	u.logger.Info("Updating to", version)

	// Download and decompress the gzipped bin and its signature.
	url := u.api.EntryLink("download")
	data, err := u.download(url + "/percona-agent-" + version + ".gz")
	if err != nil {
		return err
	}
	sig, err := u.download(url + "/percona-agent-" + version + ".sig")
	if err != nil {
		return err
	}

	// Check the binary's signature.  It's signed by Percona.
	if err = u.checkSignature(data, sig); err != nil {
		return err
	}

	// Write binary to disk as /tmp/percona-agent-<version>.
	newBin := filepath.Join(os.TempDir(), fmt.Sprintf("percona-agent-%s-%s", version, runtime.GOARCH))
	u.logger.Debug("Update:write:" + newBin)
	if err := ioutil.WriteFile(newBin, data, os.FileMode(0755)); err != nil {
		return err
	}

	// Run new binary -version to make sure it runs and returns the version.
	u.logger.Debug("Update:exec")
	out, err := exec.Command(newBin, "-version").Output()
	if err != nil {
		return err
	}
	u.logger.Debug("Update:exec:" + string(out))
	if strings.TrimSpace(string(out)) != "percona-agent "+version {
		return fmt.Errorf("%s -version returns %s, expected %s", newBin, out, version)
	}

	// Overwrite the current, running binary with new bin.
	u.logger.Info("Moving", newBin, "to", u.currentBin)
	if err := os.Rename(newBin, u.currentBin); err != nil {
		return err
	}

	u.logger.Info("Update complete; restart percona-agent")
	return nil
}

func (u *Updater) download(url string) ([]byte, error) {
	u.logger.Debug("download:call:" + url)
	defer u.logger.Debug("download:call")

	code, data, err := u.api.Get(u.api.ApiKey(), url)
	u.logger.Debug(fmt.Sprintf("download:code:%d", code))
	if err != nil {
		return nil, fmt.Errorf("GET %s error: %s", err)
	}
	if code != 200 {
		return nil, fmt.Errorf("GET %s returned %d, expected 200", url, code)
	}
	if len(data) == 0 {
		return nil, fmt.Errorf("GET %s did not return any data", url)
	}
	return data, nil
}<commit_msg>Add real Percona pub key.  Change download url.<commit_after>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package pct

import (
	"crypto"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

var PublicKey = []byte(`-----BEGIN PUBLIC KEY-----
<KEY>
-----END PUBLIC KEY-----`)

type Updater struct {
	logger         *Logger
	api            APIConnector
	currentBin     string
	currentVersion string
	// --
	rsaPubKey *rsa.PublicKey
	major     int64
	minor     int64
	patch     int64
}

func NewUpdater(logger *Logger, api APIConnector, pubKey []byte, currentBin, currentVersion string) *Updater {
	pubkeyMarshaled, _ := pem.Decode(pubKey)
	pubKeyParsed, err := x509.ParsePKIXPublicKey(pubkeyMarshaled.Bytes)
	if err != nil {
		panic(err)
	}
	rsaPubKey := pubKeyParsed.(*rsa.PublicKey)

	major, minor, patch := VersionStringToInts(currentVersion)
	u := &Updater{
		logger:         logger,
		api:            api,
		currentBin:     currentBin, // filepath.Abs(os.Args[0])
		currentVersion: currentVersion,
		// --
		rsaPubKey: rsaPubKey,
		major:     major,
		minor:     minor,
		patch:     patch,
	}
	return u
}

func (u *Updater) Check() (string, string, error) {
	url := fmt.Sprintf("%s/latest", u.api.EntryLink("download"), u.major, u.minor)
	v, err := u.download(url)
	if err != nil {
		return "", "", err
	}
	version := string(v)
	major, minor, patch := VersionStringToInts(version)
	switch {
	case major > u.major:
		return "major", version, nil
	case minor > u.minor:
		return "minor", version, nil
	case patch > u.patch:
		return "patch", version, nil
	default:
		return "", "", nil
	}
}

func (u *Updater) Update(version string) error {
	u.logger.Info("Updating to", version)

	// Download and decompress the gzipped bin and its signature.
	url := fmt.Sprintf("%s/%s/percona-agent-%s", u.api.EntryLink("download"), version, version)
	data, err := u.download(url + ".gz")
	if err != nil {
		return err
	}
	sig, err := u.download(url + ".sig")
	if err != nil {
		return err
	}

	// Check the binary's signature.  It's signed by Percona.
	if err = u.checkSignature(data, sig); err != nil {
		return err
	}

	// Write binary to disk as /tmp/percona-agent-<version>.
	newBin := filepath.Join(os.TempDir(), fmt.Sprintf("percona-agent-%s-%s", version, runtime.GOARCH))
	u.logger.Debug("Update:write:" + newBin)
	if err := ioutil.WriteFile(newBin, data, os.FileMode(0755)); err != nil {
		return err
	}

	// Run new binary -version to make sure it runs and returns the version.
	u.logger.Debug("Update:exec")
	out, err := exec.Command(newBin, "-version").Output()
	if err != nil {
		return err
	}
	u.logger.Debug("Update:exec:" + string(out))
	if strings.TrimSpace(string(out)) != "percona-agent "+version {
		return fmt.Errorf("%s -version returns %s, expected %s", newBin, out, version)
	}

	// Overwrite the current, running binary with new bin.
	u.logger.Info("Moving", newBin, "to", u.currentBin)
	if err := os.Rename(newBin, u.currentBin); err != nil {
		return err
	}

	u.logger.Info("Update complete; restart percona-agent")
	return nil
}

func (u *Updater) download(url string) ([]byte, error) {
	u.logger.Debug("download:call:" + url)
	defer u.logger.Debug("download:call")

	u.logger.Info("Downloading", url)

	code, data, err := u.api.Get(u.api.ApiKey(), url)
	u.logger.Debug(fmt.Sprintf("download:code:%d", code))
	if err != nil {
		return nil, fmt.Errorf("GET %s error: %s", err)
	}
	if code != 200 {
		return nil, fmt.Errorf("GET %s returned %d, expected 200", url, code)
	}
	if len(data) == 0 {
		return nil, fmt.Errorf("GET %s did not return any data", url)
	}
	return data, nil
}
{'fraction_non_alphanumeric': 0.14052697616060225, 'fraction_numerical': 0.006843846241587772, 'mean_word_length': 3.484910485933504, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2916507', 'n_tokens_mistral': 3123, 'n_tokens_neox': 2996, 'n_words': 933}