prepare.go (182 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package openserverless
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/Masterminds/semver"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/mitchellh/go-homedir"
)
func downloadTasksFromGitHub(force bool, silent bool) (string, error) {
debug("Download tasks from github")
repoURL := getOpsRepo()
branch := getOpsBranch()
opsDir, err := homedir.Expand("~/.ops")
if err != nil {
return "", err
}
if err := os.MkdirAll(opsDir, 0755); err != nil {
return "", err
}
opsBranchDir := joinpath(opsDir, branch)
localDir, err := homedir.Expand(joinpath(opsBranchDir, "olaris"))
if err != nil {
return "", err
}
debug("localDir", localDir)
// Updating existing tools
if exists(opsBranchDir, "olaris") {
trace("Updating olaris in", opsBranchDir)
fmt.Println("Updating tasks...")
r, err := git.PlainOpen(localDir)
if err != nil {
return "", err
}
// Get the working directory for the repository
w, err := r.Worktree()
if err != nil {
return "", err
}
// Pull the latest changes from the origin remote and merge into the current branch
// Clone the repo if not existing
ref := plumbing.NewBranchReferenceName(branch)
err = w.Pull(&git.PullOptions{
RemoteName: "origin",
ReferenceName: ref,
SingleBranch: true,
})
if err != nil {
if err.Error() == "already up-to-date" {
fmt.Println("Tasks are already up to date!")
return localDir, nil
}
return "", err
}
fmt.Println("Tasks updated successfully")
touchLatestCheckFile(joinpath(opsBranchDir, LATESTCHECK))
return localDir, nil
}
// Clone the repo if not existing
ref := plumbing.NewBranchReferenceName(branch)
cloneOpts := &git.CloneOptions{
URL: repoURL,
Progress: os.Stderr,
ReferenceName: ref, // Specify the branch to clone
}
fmt.Println("Cloning tasks...")
_, err = git.PlainClone(localDir, false, cloneOpts)
if err != nil {
os.RemoveAll(opsBranchDir)
warn(fmt.Sprintf("failed to clone olaris on branch '%s'", branch))
return "", err
}
fmt.Println("Tasks downloaded successfully")
createLatestCheckFile(opsBranchDir)
// clone
return localDir, nil
}
func pullTasks(force, silent bool) (string, error) {
// download from github
localDir, err := downloadTasksFromGitHub(force, silent)
debug("localDir", localDir)
if err != nil {
return "", fmt.Errorf("cannot update tasks because: %s\nremove the folder ~/.ops and run ops -update", err.Error())
}
err = ensurePrereq(localDir)
if err != nil {
log.Fatalf("cannot download prerequisites: %v", err)
}
// validate OpsVersion semver against opsroot.json
opsRoot, err := readOpsRootFile(localDir)
if err != nil {
return "", err
}
// check if the version is up to date
opsVersion, err := semver.NewVersion(OpsVersion)
if err != nil {
// in development mode, we don't have a valid semver version
warn("Unable to validate ops version", OpsVersion, ":", err)
return localDir, nil
}
opsRootVersion, err := semver.NewVersion(opsRoot.Version)
if err != nil {
warn("Unable to validate opsroot.json version", opsRoot.Version, ":", err)
return localDir, nil
}
// check if the version is up to date, if not warn the user
if opsVersion.LessThan(opsRootVersion) {
fmt.Println()
fmt.Printf("Your ops version (%v) is older than the required version (%v).\n", opsVersion, opsRootVersion)
if err := autoCLIUpdate(); err != nil {
return "", err
}
}
err = checkOperatorVersion(opsRoot.Config)
if err == nil {
fmt.Println()
fmt.Println("New operator version detected!")
fmt.Println("Current deployed operator can be updated with: ops update operator")
}
return localDir, nil
}
// locateOpsRoot locate the folder where starts execution
// it can be a parent folder of the current folder or it can be downloaded
// from github - it should contain a file opsfile.yml and a file opstools.yml in the root
func locateOpsRoot(cur string) (string, error) {
cur, err := filepath.Abs(cur)
if err != nil {
return "", err
}
// search the root from here
search := locateOpsRootSearch(cur)
if search != "" {
trace("found searching up:", search)
return search, nil
}
// is there olaris folder?
olaris := joinpath(cur, "olaris")
if exists(cur, "olaris") && exists(olaris, OPSFILE) && exists(olaris, OPSROOT) {
trace("found sub olaris:", olaris)
return olaris, nil
}
// is there an olaris folder in ~/.ops ?
opsOlarisDir := fmt.Sprintf("~/.ops/%s/olaris", getOpsBranch())
olaris, err = homedir.Expand(opsOlarisDir)
if err == nil && exists(olaris, OPSFILE) && exists(olaris, OPSROOT) {
trace("found sub", opsOlarisDir, ":", olaris)
return olaris, nil
}
return "", fmt.Errorf("cannot find opsfiles, download them with ops -update")
}
// locateOpsRootSearch search for `opsfiles.yml`
// and goes up looking for a folder with also `opsroot.json`
func locateOpsRootSearch(cur string) string {
debug("locateOpsRootSearch:", cur)
// exits opsfile.yml? if not, go up until you find it
if !exists(cur, OPSFILE) {
return ""
}
if exists(cur, OPSROOT) {
return cur
}
parent := parent(cur)
if parent == "" {
return ""
}
return locateOpsRootSearch(parent)
}
func autoCLIUpdate() error {
cli := os.Getenv("OPS_CMD")
trace("autoCLIUpdate", cli)
cmd := exec.Command(cli, "util", "update-cli")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func checkOperatorVersion(opsRootConfig map[string]interface{}) error {
trace("checkOperatorVersion")
images := opsRootConfig["images"].(map[string]interface{})
operator := images["operator"].(string)
opVer := strings.Split(operator, ":")[1]
cmd := exec.Command(os.Getenv("OPS_CMD"), "util", "check-operator-version", opVer)
return cmd.Run()
}
func setOpsOlarisHash(olarisDir string) error {
trace("setOpsOlarisHash", olarisDir)
r, err := git.PlainOpen(olarisDir)
if err != nil {
return err
}
h, err := r.Head()
if err != nil {
return err
}
debug("olaris hash", h.Hash().String())
os.Setenv("OPS_OLARIS", h.Hash().String())
trace("OPS_OLARIS", os.Getenv("OPS_OLARIS"))
return nil
}