Skip to content

Commit

Permalink
Use envoy v2 bootstrap for sidecar (istio#2561)
Browse files Browse the repository at this point in the history
* Add a basic kube-lego config, for automatic cert creation for SNI

* Add a half working v2 ingress, with zero vpn and sni

* Move the envoy v2 config to the proxy docker image

* Updates

* Initial agent package, using original code (without changing the source)

* Bad merge

* Remove files updated accidentally

* Simplified generation, the code was far too complex

* Add a new golden file, using pb format (less conversions, better error messages)

* Finally a working golden pb !

* Switch back to json-based config, add it to deb and docker

* Somehow grafana template got modified by test

* Move some files to separate PR

* Fix the config and tests

* Fix missing :

* Revert accidental change

* Format files with bin/fmt.sh

* Add missing license to test, rename the file since it has no dep on envoy

* Remove unused file

* Add the renamed files to git...

* Fix lint error

* Fetch logs, use json and better error message

* Add the golden files.

* Add the pilot SAN

* Go fmt and add the log, so it can be debugged

* Attempt to get istioct to accept kubeconfig

* Revert the debug messages for artifacts
  • Loading branch information
costinm authored and andraxylia committed Feb 6, 2018
1 parent cb4b526 commit aa51353
Show file tree
Hide file tree
Showing 21 changed files with 679 additions and 12 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
keys:
- dep-cache-{{ checksum "Gopkg.lock" }}
- run: make build test-bins docker.tag
- run: echo $KUBECONFIG
- run: bin/testEnvRootMinikube.sh wait
- run: docker images
- run: PATH=$GOPATH/bin:$PATH make e2e_simple E2E_ARGS="--skip_cleanup -use_local_cluster -cluster_wide -alsologtostderr -test.v -v 2"
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# Global Variables
#-----------------------------------------------------------------------------
ISTIO_GO := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
export ISTIO_GO
SHELL := /bin/bash

# Current version, updated after a release.
Expand Down
13 changes: 12 additions & 1 deletion pilot/cmd/pilot-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/spf13/cobra"

"github.com/spf13/cobra/doc"

meshconfig "istio.io/api/mesh/v1alpha1"
"istio.io/istio/pilot/cmd"
"istio.io/istio/pilot/pkg/model"
Expand Down Expand Up @@ -58,6 +59,7 @@ var (
controlPlaneAuthPolicy string
customConfigFile string
proxyLogLevel string
bootstrapv2 bool

loggingOptions = log.NewOptions()

Expand Down Expand Up @@ -196,7 +198,14 @@ var (

log.Infof("Monitored certs: %#v", certs)

envoyProxy := envoy.NewProxy(proxyConfig, role.ServiceNode(), proxyLogLevel)
var envoyProxy proxy.Proxy
if bootstrapv2 {
// Using a different constructor - the code will likely be refactored / split from the v1,
// but may expose same interface to minimize risks
envoyProxy = envoy.NewV2Proxy(proxyConfig, role.ServiceNode(), proxyLogLevel, pilotSAN)
} else {
envoyProxy = envoy.NewProxy(proxyConfig, role.ServiceNode(), proxyLogLevel)
}
agent := proxy.NewAgent(envoyProxy, proxy.DefaultRetry)
watcher := envoy.NewWatcher(proxyConfig, agent, role, certs, pilotSAN)
ctx, cancel := context.WithCancel(context.Background())
Expand Down Expand Up @@ -269,6 +278,8 @@ func init() {
proxyCmd.PersistentFlags().StringVar(&proxyLogLevel, "proxyLogLevel", "info",
fmt.Sprintf("The log level used to start the Envoy proxy (choose from {%s, %s, %s, %s, %s, %s, %s})",
"trace", "debug", "info", "warn", "err", "critical", "off"))
proxyCmd.PersistentFlags().BoolVar(&bootstrapv2, "bootstrapv2", true,
"Use bootstrap v2")

// Attach the Istio logging options to the command.
loggingOptions.AttachCobraFlags(rootCmd)
Expand Down
1 change: 1 addition & 0 deletions pilot/docker/Dockerfile.proxy
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ COPY envoy_pilot.json /etc/istio/proxy/envoy_pilot.json
COPY envoy_pilot_auth.json /etc/istio/proxy/envoy_pilot_auth.json
COPY envoy_mixer.json /etc/istio/proxy/envoy_mixer.json
COPY envoy_mixer_auth.json /etc/istio/proxy/envoy_mixer_auth.json
COPY envoy_bootstrap_tmpl.json /var/lib/istio/envoy/envoy_bootstrap_tmpl.json

ENTRYPOINT ["/usr/local/bin/pilot-agent"]
2 changes: 2 additions & 0 deletions pilot/docker/Dockerfile.proxy_debug
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ COPY envoy_pilot.json /etc/istio/proxy/envoy_pilot.json
COPY envoy_pilot_auth.json /etc/istio/proxy/envoy_pilot_auth.json
COPY envoy_mixer.json /etc/istio/proxy/envoy_mixer.json
COPY envoy_mixer_auth.json /etc/istio/proxy/envoy_mixer_auth.json
# Use the same file as the deb file.
COPY envoy_bootstrap_tmpl.json /var/lib/istio/envoy/envoy_bootstrap_tmpl.json

ENTRYPOINT ["/usr/local/bin/pilot-agent"]
1 change: 1 addition & 0 deletions pilot/pkg/proxy/envoy/v1/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func (conf *Config) Write(w io.Writer) error {

// BuildConfig creates a proxy config with discovery services and admin port
// it creates config for Ingress, Egress and Sidecar proxies
// TODO: remove after new agent package is done
func BuildConfig(config meshconfig.ProxyConfig, pilotSAN []string) *Config {
listeners := Listeners{}

Expand Down
22 changes: 21 additions & 1 deletion pilot/pkg/proxy/envoy/v1/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
meshconfig "istio.io/api/mesh/v1alpha1"
"istio.io/istio/pilot/pkg/model"
"istio.io/istio/pilot/pkg/proxy"
"istio.io/istio/pkg/bootstrap"
"istio.io/istio/pkg/log"
)

Expand Down Expand Up @@ -238,6 +239,8 @@ type envoy struct {
config meshconfig.ProxyConfig
node string
extraArgs []string
v2 bool
pilotSAN []string
}

// NewProxy creates an instance of the proxy control commands
Expand All @@ -255,6 +258,15 @@ func NewProxy(config meshconfig.ProxyConfig, node string, logLevel string) proxy
}
}

// NewV2Proxy creates an instance of the proxy using v2 bootstrap
func NewV2Proxy(config meshconfig.ProxyConfig, node string, logLevel string, pilotSAN []string) proxy.Proxy {
proxy := NewProxy(config, node, logLevel)
e := proxy.(envoy)
e.v2 = true
e.pilotSAN = pilotSAN
return e
}

func (proxy envoy) args(fname string, epoch int) []string {
startupArgs := []string{"-c", fname,
"--restart-epoch", fmt.Sprint(epoch),
Expand Down Expand Up @@ -284,7 +296,15 @@ func (proxy envoy) Run(config interface{}, epoch int, abort <-chan error) error
// Note: the cert checking still works, the generated file is updated if certs are changed.
// We just don't save the generated file, but use a custom one instead. Pilot will keep
// monitoring the certs and restart if the content of the certs changes.
if len(proxy.config.CustomConfigFile) > 0 {
if proxy.v2 {
out, err := bootstrap.WriteBootstrap(&proxy.config, epoch, proxy.pilotSAN)
if err != nil {
log.Errora("Failed to generate bootstrap config", err)
os.Exit(1) // Prevent infinite loop attempting to write the file, let k8s/systemd report
return err
}
fname = out
} else if len(proxy.config.CustomConfigFile) > 0 {
// there is a custom configuration. Don't write our own config - but keep watching the certs.
fname = proxy.config.CustomConfigFile
} else {
Expand Down
1 change: 1 addition & 0 deletions pilot/test/integration/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (a *accessLogs) check(infra *infra) error {
got := strings.Count(logs, id)
if got < want {
log.Errorf("Got %d for %s in logs of %s, want %d", got, id, pod, want)
log.Errorf("Log: %s", logs)
return errAgain
}
}
Expand Down
94 changes: 94 additions & 0 deletions pkg/bootstrap/bootstrap_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2018 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 bootstrap

import (
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"text/template"

meshconfig "istio.io/api/mesh/v1alpha1"
)

// Generate the envoy v2 bootstrap configuration, using template.
const (
// EpochFileTemplate is a template for the root config JSON
EpochFileTemplate = "envoy-rev%d.json"
DefaultCfgDir = "/var/lib/istio/envoy/envoy_bootstrap_tmpl.json"
)

func configFile(config string, epoch int) string {
return path.Join(config, fmt.Sprintf(EpochFileTemplate, epoch))
}

// WriteBootstrap generates an envoy config based on config and epoch, and returns the filename.
// TODO: in v2 some of the LDS ports (port, http_port) should be configured in the bootstrap.
func WriteBootstrap(config *meshconfig.ProxyConfig, epoch int, pilotSAN []string) (string, error) {
if err := os.MkdirAll(config.ConfigPath, 0700); err != nil {
return "", err
}
// attempt to write file
fname := configFile(config.ConfigPath, epoch)

cfg := config.CustomConfigFile
if cfg == "" {
cfg = DefaultCfgDir
}

cfgTmpl, err := ioutil.ReadFile(cfg)
if err != nil {
return "", err
}

t, err := template.New("bootstrap").Parse(string(cfgTmpl))
if err != nil {
return "", err
}

opts := map[string]interface{}{
"config": config,
}

if pilotSAN != nil {
opts["pilot_SAN"] = pilotSAN
}

// Simplify the template
opts["refresh_delay"] = fmt.Sprintf("{\"seconds\": %d, \"nanos\": %d}", config.DiscoveryRefreshDelay.Seconds, config.DiscoveryRefreshDelay.Nanos)
opts["connect_timeout"] = fmt.Sprintf("{\"seconds\": %d, \"nanos\": %d}", config.ConnectTimeout.Seconds, config.ConnectTimeout.Nanos)

addPort := strings.Split(config.DiscoveryAddress, ":")
opts["pilot_address"] = fmt.Sprintf("{\"address\": \"%s\", \"port_value\": %s}", addPort[0], addPort[1])

if config.ZipkinAddress != "" {
addPort = strings.Split(config.ZipkinAddress, ":")
opts["zipkin"] = fmt.Sprintf("{\"address\": \"%s\", \"port_value\": %s}", addPort[0], addPort[1])
}
if config.StatsdUdpAddress != "" {
addPort = strings.Split(config.StatsdUdpAddress, ":")
opts["statsd"] = fmt.Sprintf("{\"address\": \"%s\", \"port_value\": %s}", addPort[0], addPort[1])
}
fout, err := os.Create(fname)
if err != nil {
return "", err
}

// Execute needs some sort of io.Writer
err = t.Execute(fout, opts)
return fname, err
}
94 changes: 94 additions & 0 deletions pkg/bootstrap/bootstrap_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2018 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 bootstrap

import (
"io/ioutil"
"os"
"testing"

"github.com/golang/protobuf/proto"

meshconfig "istio.io/api/mesh/v1alpha1"
)

func TestGolden(t *testing.T) {
cases := []struct {
base string
}{
{
"auth",
},
{
"default",
},
{
// Specify zipkin/statsd address, similar with the default config in v1 tests
"all",
},
}

out := os.Getenv("ISTIO_OUT") // defined in the makefile
if out == "" {
out = "/tmp"
}

for _, c := range cases {
t.Run("Bootrap-"+c.base, func(t *testing.T) {
cfg, err := loadProxyConfig(c.base, out, t)
if err != nil {
t.Fatal(err)
}
fn, err := WriteBootstrap(cfg, 0, []string{
"spiffe://cluster.local/ns/istio-system/sa/istio-pilot-service-account"})
if err != nil {
t.Fatal(err)
}
real, err := ioutil.ReadFile(fn)
if err != nil {
t.Error("Error reading generated file ", err)
return
}
golden, err := ioutil.ReadFile("testdata/" + c.base + "_golden.json")
if err != nil {
golden = []byte{}
}
if string(real) != string(golden) {
t.Error("Generated incorrect config, want:\n" + string(golden) + "\ngot:\n" + string(real))
}
})
}

}

func loadProxyConfig(base, out string, t *testing.T) (*meshconfig.ProxyConfig, error) {
content, err := ioutil.ReadFile("testdata/" + base + ".proto")
if err != nil {
return nil, err
}
cfg := &meshconfig.ProxyConfig{}
err = proto.UnmarshalText(string(content), cfg)
if err != nil {
return nil, err
}

// Exported from makefile or env
cfg.ConfigPath = out + "/bootstrap/" + base
gobase := os.Getenv("ISTIO_GO")
if gobase == "" {
gobase = "../.."
}
cfg.CustomConfigFile = gobase + "/tools/deb/envoy_bootstrap_tmpl.json"
return cfg, nil
}
16 changes: 16 additions & 0 deletions pkg/bootstrap/testdata/all.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
config_path: "/etc/istio/proxy"
binary_path: "/usr/local/bin/envoy"
service_cluster: "istio-proxy"
drain_duration: {seconds: 5}
parent_shutdown_duration: {seconds: 6}
discovery_address: "mypilot:1001"
discovery_refresh_delay: {seconds: 3}
zipkin_address: "localhost:6000"
connect_timeout: {seconds: 7}
statsd_udp_address: "10.1.1.1:9125"
proxy_admin_port: 15003
availability_zone: "AZ"
control_plane_auth_policy: MUTUAL_TLS
stat_name_length: 200

# Sets all relevant options to values different than default
Loading

0 comments on commit aa51353

Please sign in to comment.