mirror of https://github.com/kubernetes/kops.git
Allow channels tool to understand well-known short references
This allows us to move them to a dedicated addons repo later.
This commit is contained in:
parent
5f4e285a2e
commit
b0f7bbbadf
|
|
@ -7,10 +7,12 @@ import (
|
||||||
"k8s.io/kops/util/pkg/tables"
|
"k8s.io/kops/util/pkg/tables"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApplyChannelCmd struct {
|
type ApplyChannelCmd struct {
|
||||||
Yes bool
|
Yes bool
|
||||||
|
Files []string
|
||||||
}
|
}
|
||||||
|
|
||||||
var applyChannel ApplyChannelCmd
|
var applyChannel ApplyChannelCmd
|
||||||
|
|
@ -28,6 +30,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().BoolVar(&applyChannel.Yes, "yes", false, "Apply update")
|
cmd.Flags().BoolVar(&applyChannel.Yes, "yes", false, "Apply update")
|
||||||
|
cmd.Flags().StringSliceVar(&applyChannel.Files, "f", []string{}, "Apply from a local file")
|
||||||
|
|
||||||
applyCmd.AddCommand(cmd)
|
applyCmd.AddCommand(cmd)
|
||||||
}
|
}
|
||||||
|
|
@ -38,32 +41,60 @@ func (c *ApplyChannelCmd) Run(args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error getting current directory: %v", err)
|
|
||||||
}
|
|
||||||
baseURL, err := url.Parse(cwd + string(os.PathSeparator))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error building url for current directory %q: %v", cwd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var addons []*channels.Addon
|
var addons []*channels.Addon
|
||||||
for _, arg := range args {
|
for _, name := range args {
|
||||||
channel, err := url.Parse(arg)
|
location, err := url.Parse(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse argument %q as url", arg)
|
return fmt.Errorf("unable to parse argument %q as url", name)
|
||||||
}
|
}
|
||||||
if !channel.IsAbs() {
|
if !location.IsAbs() {
|
||||||
channel = baseURL.ResolveReference(channel)
|
// We recognize the following "well-known" format:
|
||||||
|
// <name> with no slashes ->
|
||||||
|
if strings.Contains(name, "/") {
|
||||||
|
return fmt.Errorf("Channel format not recognized (did you mean to use `-f` to specify a local file?): %q", name)
|
||||||
|
}
|
||||||
|
expanded := "https://raw.githubusercontent.com/kubernetes/kops/master/addons/" + name + "/addon.yaml"
|
||||||
|
location, err = url.Parse(expanded)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to parse expanded argument %q as url", expanded)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
o, err := channels.LoadAddons(channel)
|
o, err := channels.LoadAddons(name, location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error loading file %q: %v", arg, err)
|
return fmt.Errorf("error loading channel: %v", location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
current, err := o.GetCurrent()
|
current, err := o.GetCurrent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error processing latest versions in %q: %v", arg, err)
|
return fmt.Errorf("error processing latest versions in %q: %v", location, err)
|
||||||
|
}
|
||||||
|
addons = append(addons, current...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range c.Files {
|
||||||
|
location, err := url.Parse(f)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to parse argument %q as url", f)
|
||||||
|
}
|
||||||
|
if !location.IsAbs() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error getting current directory: %v", err)
|
||||||
|
}
|
||||||
|
baseURL, err := url.Parse(cwd + string(os.PathSeparator))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error building url for current directory %q: %v", cwd, err)
|
||||||
|
}
|
||||||
|
location = baseURL.ResolveReference(location)
|
||||||
|
}
|
||||||
|
o, err := channels.LoadAddons(f, location)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error loading file %q: %v", f, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
current, err := o.GetCurrent()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error processing latest versions in %q: %v", f, err)
|
||||||
}
|
}
|
||||||
addons = append(addons, current...)
|
addons = append(addons, current...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,17 @@ func (c *GetAddonsCmd) Run(args []string) error {
|
||||||
}
|
}
|
||||||
return "?"
|
return "?"
|
||||||
})
|
})
|
||||||
|
t.AddColumn("CHANNEL", func(r *addonInfo) string {
|
||||||
|
if r.Version == nil {
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
if r.Version.Channel != nil {
|
||||||
|
return *r.Version.Channel
|
||||||
|
}
|
||||||
|
return "?"
|
||||||
|
})
|
||||||
|
|
||||||
columns := []string{"NAMESPACE", "NAME", "VERSION"}
|
columns := []string{"NAMESPACE", "NAME", "VERSION", "CHANNEL"}
|
||||||
err := t.Render(info, os.Stdout, columns...)
|
err := t.Render(info, os.Stdout, columns...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Addon struct {
|
type Addon struct {
|
||||||
Name string
|
Name string
|
||||||
Channel url.URL
|
ChannelName string
|
||||||
Spec *api.AddonSpec
|
ChannelLocation url.URL
|
||||||
|
Spec *api.AddonSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddonUpdate struct {
|
type AddonUpdate struct {
|
||||||
|
|
@ -22,9 +23,8 @@ type AddonUpdate struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Addon) ChannelVersion() *ChannelVersion {
|
func (a *Addon) ChannelVersion() *ChannelVersion {
|
||||||
channel := a.Channel.String()
|
|
||||||
return &ChannelVersion{
|
return &ChannelVersion{
|
||||||
Channel: &channel,
|
Channel: &a.ChannelName,
|
||||||
Version: a.Spec.Version,
|
Version: a.Spec.Version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +81,7 @@ func (a *Addon) EnsureUpdated(k8sClient *release_1_3.Clientset) (*AddonUpdate, e
|
||||||
return nil, field.Invalid(field.NewPath("Spec", "Manifest"), manifest, "Not a valid URL")
|
return nil, field.Invalid(field.NewPath("Spec", "Manifest"), manifest, "Not a valid URL")
|
||||||
}
|
}
|
||||||
if !manifestURL.IsAbs() {
|
if !manifestURL.IsAbs() {
|
||||||
manifestURL = a.Channel.ResolveReference(manifestURL)
|
manifestURL = a.ChannelLocation.ResolveReference(manifestURL)
|
||||||
}
|
}
|
||||||
glog.Infof("Applying update from %q", manifestURL)
|
glog.Infof("Applying update from %q", manifestURL)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package channels
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/golang/glog"
|
||||||
"k8s.io/kops/channels/pkg/api"
|
"k8s.io/kops/channels/pkg/api"
|
||||||
"k8s.io/kops/upup/pkg/fi/utils"
|
"k8s.io/kops/upup/pkg/fi/utils"
|
||||||
"k8s.io/kops/util/pkg/vfs"
|
"k8s.io/kops/util/pkg/vfs"
|
||||||
|
|
@ -10,20 +11,22 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Addons struct {
|
type Addons struct {
|
||||||
Channel url.URL
|
ChannelName string
|
||||||
APIObject *api.Addons
|
ChannelLocation url.URL
|
||||||
|
APIObject *api.Addons
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadAddons(location *url.URL) (*Addons, error) {
|
func LoadAddons(name string, location *url.URL) (*Addons, error) {
|
||||||
|
glog.V(2).Infof("Loading addons channel from %q", location)
|
||||||
data, err := vfs.Context.ReadFile(location.String())
|
data, err := vfs.Context.ReadFile(location.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error reading addons from %q: %v", location, err)
|
return nil, fmt.Errorf("error reading addons from %q: %v", location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseAddons(location, data)
|
return ParseAddons(name, location, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseAddons(location *url.URL, data []byte) (*Addons, error) {
|
func ParseAddons(name string, location *url.URL, data []byte) (*Addons, error) {
|
||||||
// Yaml can't parse empty strings
|
// Yaml can't parse empty strings
|
||||||
configString := string(data)
|
configString := string(data)
|
||||||
configString = strings.TrimSpace(configString)
|
configString = strings.TrimSpace(configString)
|
||||||
|
|
@ -36,7 +39,7 @@ func ParseAddons(location *url.URL, data []byte) (*Addons, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Addons{Channel: *location, APIObject: apiObject}, nil
|
return &Addons{ChannelName: name, ChannelLocation: *location, APIObject: apiObject}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Addons) GetCurrent() ([]*Addon, error) {
|
func (a *Addons) GetCurrent() ([]*Addon, error) {
|
||||||
|
|
@ -69,9 +72,10 @@ func (a *Addons) All() ([]*Addon, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
addon := &Addon{
|
addon := &Addon{
|
||||||
Channel: a.Channel,
|
ChannelName: a.ChannelName,
|
||||||
Spec: s,
|
ChannelLocation: a.ChannelLocation,
|
||||||
Name: name,
|
Spec: s,
|
||||||
|
Name: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
addons = append(addons, addon)
|
addons = append(addons, addon)
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,8 @@ package protokube
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"io/ioutil"
|
|
||||||
"k8s.io/kops/channels/pkg/channels"
|
|
||||||
"k8s.io/kops/util/pkg/vfs"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -35,75 +30,7 @@ func ApplyChannel(channel string) error {
|
||||||
// We don't embed the channels code because we expect this will eventually be part of kubectl
|
// We don't embed the channels code because we expect this will eventually be part of kubectl
|
||||||
glog.Infof("checking channel: %q", channel)
|
glog.Infof("checking channel: %q", channel)
|
||||||
|
|
||||||
// We copy the channel to a temp file because it is likely e.g. an s3 URL, which kubectl can't read
|
out, err := execChannels("apply", "channel", channel, "--v=4", "--yes")
|
||||||
|
|
||||||
location, err := url.Parse(channel)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error parsing channel location: %v", err)
|
|
||||||
}
|
|
||||||
data, err := vfs.Context.ReadFile(channel)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error reading channel: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
addons, err := channels.ParseAddons(location, data)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error parsing adddons: %v", err)
|
|
||||||
}
|
|
||||||
all, err := addons.All()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error processing adddons: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", "channel")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error creating temp dir: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := os.RemoveAll(tmpDir); err != nil {
|
|
||||||
glog.Warningf("error deleting temp dir: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
localChannelFile := path.Join(tmpDir, "channel.yaml")
|
|
||||||
if err := ioutil.WriteFile(localChannelFile, data, 0600); err != nil {
|
|
||||||
return fmt.Errorf("error writing temp file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, addon := range all {
|
|
||||||
if addon.Spec.Manifest == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
manifest := *addon.Spec.Manifest
|
|
||||||
manifestURL, err := url.Parse(manifest)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error parsing manifest location: %v", manifest)
|
|
||||||
}
|
|
||||||
if manifestURL.IsAbs() {
|
|
||||||
// Hopefully http or https!
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dest := path.Join(tmpDir, manifest)
|
|
||||||
src := location.ResolveReference(manifestURL)
|
|
||||||
|
|
||||||
b, err := vfs.Context.ReadFile(src.String())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error reading source manifest %q: %v", src, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
parent := path.Dir(dest)
|
|
||||||
if err := os.MkdirAll(parent, 0700); err != nil {
|
|
||||||
return fmt.Errorf("error creating directories %q: %v", parent, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(dest, b, 0600); err != nil {
|
|
||||||
return fmt.Errorf("error copying channel to temp file: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := execChannels("apply", "channel", localChannelFile, "--v=4", "--yes")
|
|
||||||
glog.V(4).Infof("apply channel output was: %v", out)
|
glog.V(4).Infof("apply channel output was: %v", out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue