Skip to content
This repository has been archived by the owner on Jun 14, 2018. It is now read-only.

Introduce config metadata #1049

Merged
merged 7 commits into from
Aug 17, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
update istioctl
  • Loading branch information
kyessenov committed Aug 17, 2017
commit 6d8e4a83a144cda817421d3fc035e2ad7d404ae7
4 changes: 2 additions & 2 deletions cmd/istioctl/inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ kubectl get deployment -o yaml | istioctl kube-inject -f - | kubectl apply -f -
return err
}

mesh, err := inject.GetMeshConfig(client, istioSystem, meshConfig)
mesh, err := inject.GetMeshConfig(client, namespace, meshConfig)
if err != nil {
return fmt.Errorf("Istio configuration not found. Verify istio configmap is "+
"installed in namespace %q with `kubectl get -n %s configmap istio`",
istioSystem, istioSystem)
namespace, namespace)
}
params := &inject.Params{
InitImage: inject.InitImageName(hub, tag),
Expand Down
155 changes: 42 additions & 113 deletions cmd/istioctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@
package main

import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"

"github.com/gogo/protobuf/proto"
"github.com/golang/glog"
multierror "github.com/hashicorp/go-multierror"
"github.com/spf13/cobra"
Expand All @@ -36,8 +34,8 @@ import (
)

var (
kubeconfig string
istioSystem string
kubeconfig string
namespace string

configClient model.ConfigStore

Expand Down Expand Up @@ -72,7 +70,7 @@ istioctl mixer command documentation.
configClient, err = crd.NewClient(kubeconfig, model.ConfigDescriptor{
model.RouteRule,
model.DestinationPolicy,
}, istioSystem)
})

return
},
Expand All @@ -82,8 +80,8 @@ istioctl mixer command documentation.
Use: "create",
Short: "Create policies and rules",
Example: `
istioctl create -f example-routing.yaml
`,
istioctl create -f example-routing.yaml
`,
RunE: func(c *cobra.Command, args []string) error {
if len(args) != 0 {
c.Println(c.UsageString())
Expand All @@ -97,16 +95,11 @@ istioctl mixer command documentation.
return errors.New("nothing to create")
}
for _, config := range varr {
spec, err := config.ParseSpec()
if err != nil {
return err
}
schema, _ := configClient.ConfigDescriptor().GetByType(config.Type)
rev, err := configClient.Create(spec)
rev, err := configClient.Create(config)
if err != nil {
return err
}
fmt.Printf("Created config %v %v at revision %v\n", config.Type, schema.Key(spec), rev)
fmt.Printf("Created config %v at revision %v\n", config.Key(), rev)
}

return nil
Expand All @@ -117,8 +110,8 @@ istioctl mixer command documentation.
Use: "replace",
Short: "Replace existing policies and rules",
Example: `
istioctl replace -f example-routing.yaml
`,
istioctl replace -f example-routing.yaml
`,
RunE: func(c *cobra.Command, args []string) error {
if len(args) != 0 {
c.Println(c.UsageString())
Expand All @@ -132,23 +125,20 @@ istioctl mixer command documentation.
return errors.New("nothing to replace")
}
for _, config := range varr {
spec, err := config.ParseSpec()
if err != nil {
return err
}
schema, _ := configClient.ConfigDescriptor().GetByType(config.Type)
// fill up revision
if config.Revision == "" {
_, _, rev := configClient.Get(config.Type, schema.Key(spec))
config.Revision = rev
if config.ResourceVersion == "" {
current, exists := configClient.Get(config.Type, config.Name, config.Namespace)
if exists {
config.ResourceVersion = current.ResourceVersion
}
}

newRev, err := configClient.Update(spec, config.Revision)
newRev, err := configClient.Update(config)
if err != nil {
return err
}

fmt.Printf("Updated config %v %v to revision %v\n", config.Type, schema.Key(spec), newRev)
fmt.Printf("Updated config %v to revision %v\n", config.Key(), newRev)
}

return nil
Expand Down Expand Up @@ -183,17 +173,12 @@ istioctl mixer command documentation.

var configs []model.Config
if len(args) > 1 {
config, exists, rev := configClient.Get(typ.Type, args[1])
config, exists := configClient.Get(typ.Type, args[1], namespace)
if exists {
configs = append(configs, model.Config{
Type: typ.Type,
Key: typ.Key(config),
ResourceVersion: rev,
Spec: config,
})
configs = append(configs, *config)
}
} else {
configs, err = configClient.List(typ.Type)
configs, err = configClient.List(typ.Type, namespace)
if err != nil {
return err
}
Expand Down Expand Up @@ -242,8 +227,7 @@ istioctl mixer command documentation.
return err
}
for i := 1; i < len(args); i++ {
key := args[i]
if err := configClient.Delete(typ.Type, key); err != nil {
if err := configClient.Delete(typ.Type, args[i], namespace); err != nil {
errs = multierror.Append(errs,
fmt.Errorf("cannot delete %s: %v", args[i], err))
} else {
Expand All @@ -268,16 +252,10 @@ istioctl mixer command documentation.
var errs error
for _, config := range varr {
// compute key if necessary
if config.Key == "" {
if spec, specErr := config.ParseSpec(); specErr == nil {
schema, _ := configClient.ConfigDescriptor().GetByType(config.Type)
config.Key = schema.Key(spec)
}
}
if err = configClient.Delete(config.Type, config.Key); err != nil {
errs = multierror.Append(errs, fmt.Errorf("cannot delete %s: %v", config.Key, err))
if err = configClient.Delete(config.Type, config.Name, config.Namespace); err != nil {
errs = multierror.Append(errs, fmt.Errorf("cannot delete %s: %v", config.Key(), err))
} else {
fmt.Printf("Deleted config: %v %v\n", config.Type, config.Key)
fmt.Printf("Deleted config: %v\n", config.Key())
}
}
return errs
Expand All @@ -301,8 +279,8 @@ func init() {
}
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "c", defaultKubeconfig,
"Kubernetes configuration file")
rootCmd.PersistentFlags().StringVarP(&istioSystem, "namespace", "n", v1.NamespaceDefault,
"Kubernetes Istio system namespace")
rootCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", v1.NamespaceDefault,
"Config namespace")

postCmd.PersistentFlags().StringVarP(&file, "file", "f", "",
"Input file with the content of the configuration objects (if not set, command reads from the standard input)")
Expand All @@ -329,58 +307,17 @@ func main() {

// The schema is based on the kind (for example "route-rule" or "destination-policy")
func schema(typ string) (model.ProtoSchema, error) {
var singularForm = map[string]string{
"route-rules": "route-rule",
"destination-policies": "destination-policy",
}
if singular, ok := singularForm[typ]; ok {
typ = singular
}

out, ok := configClient.ConfigDescriptor().GetByType(typ)
if !ok {
return model.ProtoSchema{}, fmt.Errorf("Istio doesn't have configuration type %s, the types are %v",
typ, strings.Join(configClient.ConfigDescriptor().Types(), ", "))
}

return out, nil
}

// Config is the complete configuration including a parsed spec
type Config struct {
// Type SHOULD be one of the kinds in model.IstioConfig; a route-rule, ingress-rule, or destination-policy
Type string `json:"type,omitempty"`
// Key is the unique key per type
Key string `json:"key,omitempty"`
// Revision is optional for updating configs
Revision string `json:"revision,omitempty"`
// Spec is the content of the config
Spec interface{} `json:"spec,omitempty"`
}

// ParseSpec takes the field in the config object and parses into a protobuf message
// Then assigns it to the ParseSpec field
func (c *Config) ParseSpec() (proto.Message, error) {
byteSpec, err := json.Marshal(c.Spec)
if err != nil {
return nil, fmt.Errorf("could not encode Spec: %v", err)
}
schema, ok := model.IstioConfigTypes.GetByType(c.Type)
if !ok {
return nil, fmt.Errorf("unknown spec type %s", c.Type)
}
message, err := schema.FromJSON(string(byteSpec))
if err != nil {
return nil, fmt.Errorf("cannot parse proto message: %v", err)
}
if err = schema.Validate(message); err != nil {
return nil, err
for _, desc := range configClient.ConfigDescriptor() {
if desc.Type == typ || desc.Plural == typ {
return desc, nil
}
}
return message, nil
return model.ProtoSchema{}, fmt.Errorf("Istio doesn't have configuration type %s, the types are %v",
typ, strings.Join(configClient.ConfigDescriptor().Types(), ", "))
}

// readInputs reads multiple documents from the input and checks with the schema
func readInputs() ([]Config, error) {
func readInputs() ([]model.Config, error) {
var reader io.Reader
var err error

Expand All @@ -393,12 +330,12 @@ func readInputs() ([]Config, error) {
}
}

var varr []Config
var varr []model.Config

// We store route-rules as a YaML stream; there may be more than one decoder.
yamlDecoder := kubeyaml.NewYAMLOrJSONDecoder(reader, 512*1024)
for {
v := Config{}
v := model.JSONConfig{}
err = yamlDecoder.Decode(&v)

if err == io.EOF {
Expand All @@ -408,7 +345,12 @@ func readInputs() ([]Config, error) {
return nil, fmt.Errorf("cannot parse proto message: %v", err)
}

varr = append(varr, v)
config, err := model.IstioConfigTypes.FromJSON(v)
if err != nil {
return nil, fmt.Errorf("cannot parse proto message: %v", err)
}

varr = append(varr, *config)
}
glog.V(2).Infof("parsed %d inputs", len(varr))

Expand All @@ -425,21 +367,8 @@ func printShortOutput(configList []model.Config) {
// Print as YAML
func printYamlOutput(configList []model.Config) {
for _, c := range configList {
schema, _ := configClient.ConfigDescriptor().GetByType(c.Type)
out, err := schema.ToYAML(c.Spec)
if err != nil {
glog.Warning(err)
}
fmt.Printf("type: %s\n", c.Type)
fmt.Printf("key: %s\n", c.Key)
fmt.Printf("revision: %s\n", c.ResourceVersion)
fmt.Println("spec:")
lines := strings.Split(out, "\n")
for _, line := range lines {
if line != "" {
fmt.Printf(" %s\n", line)
}
}
yaml, _ := configClient.ConfigDescriptor().ToYAML(c)
fmt.Println(yaml)
fmt.Println("---")
}
}
2 changes: 1 addition & 1 deletion cmd/istioctl/mixer.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ for a description of Mixer configuration's scope, subject, and rules.

mixerRESTRequester = &k8sRESTRequester{
client: client,
namespace: istioSystem,
namespace: namespace,
service: istioMixerAPIService,
}

Expand Down
13 changes: 7 additions & 6 deletions model/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,25 @@ import (
// last update operation on the object.
type ConfigMeta struct {
// Type is a short configuration name that matches the content message type
Type string
// (e.g. "route-rule")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are type names normalized to non-k8s specific names? e.g. route-rule instead of route-rule.config.istio.io?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, type is route-rule, API is route-rules.config.istio.io/v1alpha2.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move to v1beta1 ? claim beta release with alpha api version is odd.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRDs may only support one version at a time and mixer already started using v1alpha2

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chatted with @geeknoid. Let's please change everywhere to v1beta1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use v1beta1 then by k8s conventions, this API will provide future backwards compatibility, @geeknoid. As long as we're ok taking on the work to maintain it forever, i'm OK changing to v1beta1.

Type string `json:"type,omitempty"`

// Name is a unique immutable identifier in a namespace
Name string
Name string `json:"name,omitempty"`

// Namespace defines the space for names (optional for some types),
// applications may choose to use namespaces for a variety of purposes
// (security domains, fault domains, organizational domains)
Namespace string
Namespace string `json:"namespace,omitempty"`

// Map of string keys and values that can be used to organize and categorize
// (scope and select) objects.
Labels map[string]string
Labels map[string]string `json:"labels,omitempty"`

// Annotations is an unstructured key value map stored with a resource that may be
// set by external tools to store and retrieve arbitrary metadata. They are not
// queryable and should be preserved when modifying objects.
Annotations map[string]string
Annotations map[string]string `json:"annotations,omitempty"`

// ResourceVersion is an opaque identifier for tracking updates to the config registry.
// The implementation may use a change index or a commit log for the revision.
Expand All @@ -61,7 +62,7 @@ type ConfigMeta struct {
//
// An empty revision carries a special meaning that the associated object has
// not been stored and assigned a revision.
ResourceVersion string
ResourceVersion string `json:"resourceVersion,omitempty"`
}

// Config is a configuration unit consisting of the type of configuration, the
Expand Down
Loading