Skip to content
This repository has been archived by the owner on Sep 18, 2020. It is now read-only.

Commit

Permalink
cli: add support for torcx remotes
Browse files Browse the repository at this point in the history
  • Loading branch information
lucab committed Jun 12, 2018
1 parent 3278581 commit 6d01aa7
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 7 deletions.
2 changes: 1 addition & 1 deletion internal/cli/common.go
Expand Up @@ -84,7 +84,7 @@ func fillCommonRuntime(OsRelease string) (*torcx.CommonConfig, error) {

// Add user and runtime store paths (versioned first)
if OsRelease != "" {
commonCfg.StorePaths = append(commonCfg.StorePaths, filepath.Join(commonCfg.BaseDir, "store", OsRelease, ""))
commonCfg.StorePaths = append(commonCfg.StorePaths, filepath.Join(commonCfg.BaseDir, "store", OsRelease))
}
commonCfg.StorePaths = append(commonCfg.StorePaths, filepath.Join(commonCfg.BaseDir, "store"))
extraStorePaths := viper.GetStringSlice("storepath")
Expand Down
3 changes: 2 additions & 1 deletion internal/cli/common_test.go
Expand Up @@ -63,12 +63,12 @@ func TestFillCommon(t *testing.T) {
if err := os.Setenv("TORCX_USR_MOUNTPOINT", tt.usrEnv); err != nil {
t.Fatalf("failed to set env: %s", err)
}
defer os.Unsetenv("TORCX_USR_MOUNTPOINT")
}
viper.SetEnvPrefix("TORCX")
viper.AutomaticEnv()

cfg, err := fillCommonRuntime("")
os.Unsetenv("TORCX_USR_MOUNTPOINT")
if tt.isErr {
if err == nil {
t.Fatal("expected error, got nil")
Expand Down Expand Up @@ -147,5 +147,6 @@ func TestHasExpFeature(t *testing.T) {
t.Errorf("Testcase %q failed, expected %t got %t", key, expFeat, gotFeat)

}
os.Unsetenv(envKey)
}
}
40 changes: 35 additions & 5 deletions internal/cli/profile_check.go
Expand Up @@ -16,12 +16,13 @@ package cli

import (
"fmt"
"strconv"

"github.com/coreos/torcx/internal/torcx"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/coreos/torcx/internal/torcx"
"github.com/spf13/viper"
)

var (
Expand All @@ -32,25 +33,44 @@ var (
RunE: runProfileCheck,
}

flagProfileCheckName string
flagProfileCheckPath string
flagProfileCheckOsVersion string
flagProfileCheckName string
flagProfileCheckPath string
flagProfileCheckRemoteOnly string
flagProfileCheckOsVersion string
)

func init() {
cmdProfile.AddCommand(cmdProfileCheck)
cmdProfileCheck.Flags().StringVar(&flagProfileCheckName, "name", "", "profile name to check")
cmdProfileCheck.Flags().StringVar(&flagProfileCheckPath, "file", "", "profile file to check")
cmdProfileCheck.Flags().StringVar(&flagProfileCheckRemoteOnly, "remote-only", "", "whether to only check addons with an explicit remote")
cmdProfileCheck.Flags().StringVarP(&flagProfileCheckOsVersion, "os-release", "n", "", "override OS version")
}

func parseFlagRemoteOnly() bool {
remoteOnly := false
env, ok := viper.Get("CHECK_REMOTE_ONLY").(string)
if ok && env != "" {
if value, err := strconv.ParseBool(env); err == nil {
remoteOnly = value
}
}
if flagProfileCheckRemoteOnly != "" {
if value, err := strconv.ParseBool(flagProfileCheckRemoteOnly); err == nil {
remoteOnly = value
}
}
return remoteOnly
}

func runProfileCheck(cmd *cobra.Command, args []string) error {
var err error

commonCfg, err := fillCommonRuntime(flagProfileCheckOsVersion)
if err != nil {
return errors.Wrap(err, "common configuration failed")
}
remoteOnly := parseFlagRemoteOnly()

if len(args) != 0 {
return cmd.Usage()
Expand Down Expand Up @@ -101,18 +121,28 @@ func runProfileCheck(cmd *cobra.Command, args []string) error {

missing := false
for _, im := range profile {
if remoteOnly && im.Remote == "" {
logrus.WithFields(logrus.Fields{
"name": im.Name,
"references": im.Reference,
"remote": im.Remote,
}).Debug("skipping remoteless image")
continue
}
ar, err := storeCache.ArchiveFor(im)
if err != nil {
missing = true
logrus.WithFields(logrus.Fields{
"name": im.Name,
"reference": im.Reference,
"remote": im.Remote,
}).Error("image/reference not found")
} else {
logrus.WithFields(logrus.Fields{
"name": im.Name,
"references": im.Reference,
"archive path": ar.Filepath,
"remote": im.Remote,
}).Debug("image/reference found")
}
}
Expand Down
164 changes: 164 additions & 0 deletions internal/cli/profile_populate.go
@@ -0,0 +1,164 @@
// Copyright 2018 CoreOS Inc.
//
// Licensed 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 cli

import (
"context"
"fmt"
"os"
"time"

"github.com/coreos/torcx/internal/torcx"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var (
cmdProfilePopulate = &cobra.Command{
Use: "populate",
Short: "Populates the store with all image required by the given profile",
RunE: runProfilePopulate,
}

flagProfilePopulateName string
flagProfilePopulatePath string
flagProfilePopulateOsVersion string

// TODO(lucab): consider whether to make this configurable
timeoutMins = 1
)

func init() {
cmdProfile.AddCommand(cmdProfilePopulate)
cmdProfilePopulate.Flags().StringVar(&flagProfilePopulateName, "name", "", "profile name to populate")
cmdProfilePopulate.Flags().StringVar(&flagProfilePopulatePath, "file", "", "profile file to populate")
cmdProfilePopulate.Flags().StringVarP(&flagProfilePopulateOsVersion, "os-release", "n", "", "override OS version")
}

func runProfilePopulate(cmd *cobra.Command, args []string) error {
commonCfg, err := fillCommonRuntime(flagProfilePopulateOsVersion)
if err != nil {
return errors.Wrap(err, "common configuration failed")
}

if len(args) != 0 {
return cmd.Usage()
}

if flagProfilePopulatePath == "" {
if flagProfilePopulateName == "" {
flagProfilePopulateName, err = commonCfg.NextProfileName()
if err != nil {
return errors.Wrapf(err, "unable to determine next profile")
}

logrus.Infof("using next profile %q", flagProfilePopulateName)

if flagProfilePopulateName == torcx.VendorProfileName {
logrus.Warn("using default profile (%s), which should not require external images", torcx.VendorProfileName)
}
}

localProfiles, err := torcx.ListProfiles(commonCfg.ProfileDirs())
if err != nil {
return errors.Wrap(err, "profiles listing failed")
}

var ok bool
flagProfilePopulatePath, ok = localProfiles[flagProfilePopulateName]

if !ok {
return fmt.Errorf("profile %q not found", flagProfilePopulateName)
}
}

profile, err := torcx.ReadProfilePath(flagProfilePopulatePath)
if err != nil {
return err
}

// Empty profiles are allowed
if len(profile) == 0 {
logrus.Warn("profile specifies no images")
return nil
}

storeCache, err := torcx.NewStoreCache(commonCfg.StorePaths)
if err != nil {
return err
}

remotes := []string{}
{
keys := map[string]bool{}
for _, im := range profile {
if im.Remote == "" {
continue
}
if ok := keys[im.Remote]; ok {
continue
}
remotes = append(remotes, im.Remote)
keys[im.Remote] = true
}
}

// Nothing to fetch
if len(remotes) <= 0 {
logrus.Warn("profile references no remote images")
return nil
}

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutMins)*time.Minute)
defer cancel()
remotesCache, err := torcx.NewRemotesCache(ctx, commonCfg.UsrDir, commonCfg.RemotesDirs(), remotes)
if err != nil {
return err
}

versionedStorePath := commonCfg.UserStorePath(flagProfilePopulateOsVersion)
if err := os.MkdirAll(versionedStorePath, 0755); err != nil {
return err
}

localCount := 0
remoteCount := 0
// TODO(lucab): parallelize this
for _, im := range profile {
if archive, err := storeCache.ArchiveFor(im); err == nil {
logrus.WithFields(logrus.Fields{
"path": archive.Filepath,
}).Info("image found locally")
localCount++
continue
}

ctxTo, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutMins)*time.Minute)
defer cancel()
if err := remotesCache.FetchImage(ctxTo, im, versionedStorePath); err != nil {
return err
}
remoteCount++
}

logrus.WithFields(logrus.Fields{
"local": localCount,
"downloaded": remoteCount,
"profile_name": flagProfilePopulateName,
"profile_path": flagProfilePopulatePath,
}).Info("store populated")
return nil
}
4 changes: 4 additions & 0 deletions internal/cli/torcx.go
Expand Up @@ -15,6 +15,8 @@
package cli

import (
"strings"

"github.com/coreos/torcx/pkg/multicall"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -42,6 +44,8 @@ var (
func Init() error {
viper.SetEnvPrefix("TORCX")
viper.AutomaticEnv()
replacer := strings.NewReplacer("-", "_")
viper.SetEnvKeyReplacer(replacer)

logrus.SetLevel(logrus.WarnLevel)

Expand Down

0 comments on commit 6d01aa7

Please sign in to comment.