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

Implement Istio sidecar initializer #1041

Merged
merged 20 commits into from
Aug 18, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
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
3 changes: 2 additions & 1 deletion bin/push-docker
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ bazel build --output_groups=static //cmd/... //test/...
\cp -f "${bin}/test/server/server.static" docker/server
\cp -f "${bin}/cmd/pilot-agent/pilot-agent.static" docker/pilot-agent
\cp -f "${bin}/cmd/pilot-discovery/pilot-discovery.static" docker/pilot-discovery
\cp -f "${bin}/cmd/sidecar-initializer/sidecar-initializer.static" docker/sidecar-initializer

# Build and push images

IFS=',' read -ra tags <<< "${tags}"
IFS=',' read -ra hubs <<< "${hubs}"

pushd docker
for image in app proxy proxy_init proxy_debug pilot; do
for image in app proxy proxy_init proxy_debug pilot sidecar_initializer; do
local_image="${image}:${local_tag}"
docker build -q -f "Dockerfile.${image}" -t "${local_image}" .
for tag in ${tags[@]}; do
Expand Down
24 changes: 24 additions & 0 deletions cmd/sidecar-initializer/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
Copy link
Member

Choose a reason for hiding this comment

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

missing copyright?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We haven't been adding copyright to the BUILD files.

$ find -name BUILD | xargs grep Copyright | wc -l                                                                                                              
0


go_library(
name = "go_default_library",
srcs = ["main.go"],
visibility = ["//visibility:private"],
deps = [
"//cmd:go_default_library",
"//platform/kube:go_default_library",
"//platform/kube/inject:go_default_library",
"//tools/version:go_default_library",
"@com_github_davecgh_go_spew//spew:go_default_library",
"@com_github_golang_glog//:go_default_library",
"@com_github_hashicorp_go_multierror//:go_default_library",
"@com_github_spf13_cobra//:go_default_library",
"@io_k8s_api//core/v1:go_default_library",
],
)

go_binary(
name = "sidecar-initializer",
library = ":go_default_library",
visibility = ["//visibility:public"],
)
111 changes: 111 additions & 0 deletions cmd/sidecar-initializer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2017 Istio Authors
//
// 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 main

import (
"fmt"
"os"
"time"

"k8s.io/api/core/v1"

"istio.io/pilot/cmd"
"istio.io/pilot/platform/kube"
"istio.io/pilot/platform/kube/inject"
"istio.io/pilot/tools/version"

"github.com/davecgh/go-spew/spew"
"github.com/golang/glog"
multierror "github.com/hashicorp/go-multierror"
"github.com/spf13/cobra"
)

func getRootCmd() *cobra.Command {
flags := struct {
kubeconfig string
meshconfig string
hub string
tag string
namespace string
Copy link
Member

Choose a reason for hiding this comment

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

Does this enable initializer for a given namespace? If so, won't we need one initializer per namespace? Is there a possibility to watch some config map or something to pick up namespaces enabled for Istio?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The initializer is enabled with the InitializerConfiguration resource. InitializerConfiguration is cluster scoped and there is no way through that to restrict which namespace(s) require initialization - its all or nothing. Some proposals to fix this problem are documented here but nothing is designed/implemented.

This namespace flag determines which namespace the initializer deployment manages. This is useful (required?) in our shared per-namespace e2e tests. In this case we would need one initializer deployment per namespace. Once cluster-wide Istio is the standard this can default to v1.NameSpaceAll and the single initializer deployment would be responsible for the entire cluster.

policy string
resyncPeriod time.Duration
}{}

root := &cobra.Command{
Copy link
Member

Choose a reason for hiding this comment

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

s/root/rootCmd

Use: "sidecar-initializer",
Short: "Kubernetes initializer for Istio sidecar",
RunE: func(*cobra.Command, []string) error {
switch inject.InjectionPolicy(flags.policy) {
case inject.InjectionPolicyOff, inject.InjectionPolicyOptIn, inject.InjectionPolicyOptOut:
default:
return fmt.Errorf("unknown namespace injection policy: %v", flags.policy)
Copy link
Member

Choose a reason for hiding this comment

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

nit. unknown injection policy. (omit the namespace)

}

client, err := kube.CreateInterface(flags.kubeconfig)
if err != nil {
return multierror.Prefix(err, "failed to connect to Kubernetes API.")
}

glog.V(2).Infof("version %s", version.Line())
glog.V(2).Infof("flags %s", spew.Sdump(flags))

// receive mesh configuration
mesh, err := cmd.ReadMeshConfig(flags.meshconfig)
if err != nil {
return multierror.Prefix(err, "failed to read mesh configuration.")
}

options := inject.InitializerOptions{
ResyncPeriod: flags.resyncPeriod,
Hub: flags.hub,
Tag: flags.tag,
Namespace: flags.namespace,

InjectionPolicy: inject.InjectionPolicy(flags.policy),
}
initializer := inject.NewInitializer(client, mesh, options)

stop := make(chan struct{})
go initializer.Run(stop)
cmd.WaitSignal(stop)

return nil
},
}

root.PersistentFlags().StringVar(&flags.hub, "hub", "docker.io/istio", "Docker hub")
root.PersistentFlags().StringVar(&flags.tag, "tag", "0.1", "Docker tag")
Copy link
Contributor

Choose a reason for hiding this comment

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

Make 0.2 default since this is unlikely to work with 0.1.

root.PersistentFlags().StringVar(&flags.namespace, "namespace",
v1.NamespaceDefault, "Namespace managed by initializer")
Copy link
Contributor

Choose a reason for hiding this comment

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

Should "all" be default?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went back and forth on the default here ."all" is a reasonable default for cluster-wide installations of Istio. It causes problems for namespace isolated installs. For internal testing we override it anyway so maybe its reasonable for cluster-wide installs to leave this as "all" to avoid broken deployments.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's use "all". "default" is not really default in the k8s context.

root.PersistentFlags().StringVar(&flags.policy, "policy",
string(inject.InjectionPolicyOff), "default injection policy")

root.PersistentFlags().StringVar(&flags.kubeconfig, "kubeconfig", "",
"Use a Kubernetes configuration file instead of in-cluster configuration")
root.PersistentFlags().StringVar(&flags.meshconfig, "meshConfig", "/etc/istio/config/mesh",
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I call it "meshconfig" in other places.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discovery and Istioctl use "meshConfig". The agent uses "meshconfig". I'm happy to make this consistent with either - let me know :)

Copy link
Contributor

Choose a reason for hiding this comment

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

let's rename both to meshconfig to be consistent with kubeconfig.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to meshconfig for the initializer command. I can change the others in a follow-up PR.

fmt.Sprintf("File name for Istio mesh configuration"))
root.PersistentFlags().DurationVar(&flags.resyncPeriod, "resync", time.Duration(0),
Copy link
Contributor

Choose a reason for hiding this comment

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

is 0 resync reliable enough? maybe make it a few minutes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know. I'll increase to 30 seconds just in case.

Copy link
Contributor

Choose a reason for hiding this comment

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

for cluster-wide, probably 5 minutes is enough time not to overwhelm the config server.

"Initializers resync interval")

cmd.AddFlags(root)

return root
}

func main() {
Copy link
Member

Choose a reason for hiding this comment

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

Just nit, is it possible to update this file a bit to keep consistent with other files as following format?

var(
    variables here
)

func init() {
    cmd.PersistentFlags init
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		glog.Error(err)
		os.Exit(-1)
	}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I prefer this approach which limits the use of global variables.

if err := getRootCmd().Execute(); err != nil {
os.Exit(-1)
}
}
1 change: 1 addition & 0 deletions docker/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ client
server
pilot-agent
pilot-discovery
sidecar-initializer
3 changes: 3 additions & 0 deletions docker/Dockerfile.sidecar_initializer
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM scratch
ADD sidecar-initializer /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/sidecar-initializer"]
Copy link
Contributor

Choose a reason for hiding this comment

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

You probably want some TLS certs here (assuming we need to talk to something other than kube API)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The current version only ever interacts with the k8s apiserver via patch/list/watch. It uses the in-cluster config similar to pilot discovery service.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, then it's fine. Running out of cluster would not use docker anyways.

24 changes: 22 additions & 2 deletions platform/kube/inject/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,53 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["inject.go"],
srcs = [
"initializer.go",
"inject.go",
],
visibility = ["//visibility:public"],
deps = [
"//model:go_default_library",
"//proxy:go_default_library",
"//tools/version:go_default_library",
"@com_github_ghodss_yaml//:go_default_library",
"@com_github_golang_glog//:go_default_library",
"@com_github_hashicorp_go_multierror//:go_default_library",
"@io_istio_api//:go_default_library",
"@io_k8s_api//apps/v1beta1:go_default_library",
"@io_k8s_api//batch/v1:go_default_library",
"@io_k8s_api//core/v1:go_default_library",
"@io_k8s_api//extensions/v1beta1:go_default_library",
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
"@io_k8s_apimachinery//pkg/fields:go_default_library",
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
"@io_k8s_apimachinery//pkg/types:go_default_library",
"@io_k8s_apimachinery//pkg/util/intstr:go_default_library",
"@io_k8s_apimachinery//pkg/util/strategicpatch:go_default_library",
"@io_k8s_apimachinery//pkg/util/yaml:go_default_library",
"@io_k8s_apimachinery//pkg/watch:go_default_library",
"@io_k8s_client_go//kubernetes:go_default_library",
"@io_k8s_client_go//tools/cache:go_default_library",
],
)

go_test(
name = "go_default_test",
size = "small",
srcs = ["inject_test.go"],
srcs = [
"initializer_test.go",
"inject_test.go",
],
data = glob(["testdata/*.yaml*"]),
library = ":go_default_library",
deps = [
"//platform/kube:go_default_library",
"//proxy:go_default_library",
"//test/util:go_default_library",
"@com_github_ghodss_yaml//:go_default_library",
"@io_istio_api//:go_default_library",
"@io_k8s_api//apps/v1beta1:go_default_library",
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
"@io_k8s_client_go//kubernetes:go_default_library",
],
)
Loading