Repository: kumahq/kuma
Pull request #1051 (2020-10-06 07:58:33)

chore(*) release 0.7.2

Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>





Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5a1b71962..cc4433071 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,40 @@
 # CHANGELOG
 
+## [0.7.2]
+
+* feat: fix CNI with the latest changes and bump the CNI image to 0.0.2 [#1049](https://github.com//kumahq/kuma/pull/1049) [#1043](https://github.com//kumahq/kuma/pull/1043) 
+ 👍contributed by @jakubdyszkiewicz
+* feat: exclude traffic interceptions on port using annotations [#1046](https://github.com//kumahq/kuma/pull/1046) 
+ 👍contributed by @jakubdyszkiewicz
+* feat: central place for creating defaults [#1017](https://github.com//kumahq/kuma/pull/1017) 
+ 👍contributed by @jakubdyszkiewicz
+* fix: metric to DP-CP connection should rely on control_plane.connected_state [#1009](https://github.com//kumahq/kuma/pull/1009) 
+ 👍contributed by @jakubdyszkiewicz
+* fix: use not deprecated value to disable auth on universal [#1008](https://github.com//kumahq/kuma/pull/1008) 
+ 👍contributed by @jakubdyszkiewicz
+* fix: signing token in multizone [#1007](https://github.com//kumahq/kuma/pull/1007) 
+ 👍contributed by @jakubdyszkiewicz
+* Generate inbound/outbound for HTTP/2 [#998](https://github.com//kumahq/kuma/pull/998) 
+ 👍contributed by @nickolaev
+* feat: cleanup dataplanes after 3d of the offline state [#987](https://github.com//kumahq/kuma/pull/987) 
+ 👍contributed by @lobkovilya
+* feat: validate zone location apply [#986](https://github.com//kumahq/kuma/pull/986) 
+ 👍contributed by @jakubdyszkiewicz
+* feat: change failpolicy of service hook to ignore [#983](https://github.com//kumahq/kuma/pull/983) 
+ 👍contributed by @jakubdyszkiewicz
+* fix: direct access for ingress [#985](https://github.com//kumahq/kuma/pull/985) 
+ 👍contributed by @lobkovilya
+* feat: retry connection to the CP and for fetching bootstrap [#982](https://github.com//kumahq/kuma/pull/982) 
+ 👍contributed by @jakubdyszkiewicz
+* fix: ignore services without selectors [#978](https://github.com//kumahq/kuma/pull/978) 
+ 👍contributed by @jakubdyszkiewicz
+* feat: parametrize kuma deploy [#973](https://github.com//kumahq/kuma/pull/973) 
+ 👍contributed by @jakubdyszkiewicz
+* fix: zone insights manager and limits [#976](https://github.com//kumahq/kuma/pull/976) 
+ 👍contributed by @nickolaev
+* feat: validate zone and global addresses [#967](https://github.com//kumahq/kuma/pull/967) 
+ 👍contributed by @jakubdyszkiewicz
+
 ## [0.7.1]
 > Released on  2020/08/12
 
@@ -101,7 +136,7 @@ Changes:
  
 * chore: skip Ingress endpoint if mTLS is off [#925](https://github.com//kumahq/kuma/pull/925) 
 
-*  fix(kuma-cp): Add the permissions to create and patch events [#884](https://github.com//kumahq/kuma/pull/884) 
+*  fix: Add the permissions to create and patch events [#884](https://github.com//kumahq/kuma/pull/884) 
  👍contributed by @andrew-teirney
 
 *  feat(kumactl) add install loki for log aggregation [#820](https://github.com//kumahq/kuma/pull/820) 
diff --git a/app/kuma-dp/cmd/run.go b/app/kuma-dp/cmd/run.go
index 436f71d86..98e412348 100644
--- a/app/kuma-dp/cmd/run.go
+++ b/app/kuma-dp/cmd/run.go
@@ -1,22 +1,22 @@
 package cmd
 
 import (
+	"context"
 	"crypto/tls"
 	"io/ioutil"
 	"net/http"
 	"os"
 	"time"
 
-	kuma_version "github.com/kumahq/kuma/pkg/version"
-
-	"github.com/kumahq/kuma/pkg/catalog/client"
-
 	"github.com/pkg/errors"
+	"github.com/sethvargo/go-retry"
 	"github.com/spf13/cobra"
 
 	kumadp_config "github.com/kumahq/kuma/app/kuma-dp/pkg/config"
 	"github.com/kumahq/kuma/app/kuma-dp/pkg/dataplane/accesslogs"
 	"github.com/kumahq/kuma/app/kuma-dp/pkg/dataplane/envoy"
+	"github.com/kumahq/kuma/pkg/catalog"
+	"github.com/kumahq/kuma/pkg/catalog/client"
 	"github.com/kumahq/kuma/pkg/config"
 	kuma_dp "github.com/kumahq/kuma/pkg/config/app/kuma-dp"
 	config_types "github.com/kumahq/kuma/pkg/config/types"
@@ -24,6 +24,7 @@ import (
 	"github.com/kumahq/kuma/pkg/core/runtime/component"
 	leader_memory "github.com/kumahq/kuma/pkg/plugins/leader/memory"
 	util_net "github.com/kumahq/kuma/pkg/util/net"
+	kuma_version "github.com/kumahq/kuma/pkg/version"
 )
 
 type CatalogClientFactory func(string) (client.CatalogClient, error)
@@ -51,20 +52,16 @@ func newRunCmd() *cobra.Command {
 				runLog.Error(err, "unable to load configuration")
 				return err
 			}
-			if conf, err := config.ToYAML(&cfg); err == nil {
+			if conf, err := config.ToJson(&cfg); err == nil {
 				runLog.Info("effective configuration", "config", string(conf))
 			} else {
 				runLog.Error(err, "unable to format effective configuration", "config", cfg)
 				return err
 			}
 
-			catalogClient, err := catalogClientFactory(cfg.ControlPlane.ApiServer.URL)
-			if err != nil {
-				return errors.Wrap(err, "could not create catalog client")
-			}
-			catalog, err := catalogClient.Catalog()
+			catalog, err := fetchCatalog(cfg)
 			if err != nil {
-				return errors.Wrap(err, "could retrieve catalog")
+				return err
 			}
 			if catalog.Apis.DataplaneToken.Enabled() {
 				if cfg.DataplaneRuntime.TokenPath == "" {
@@ -102,7 +99,7 @@ func newRunCmd() *cobra.Command {
 			}
 
 			dataplane, err := envoy.New(envoy.Opts{
-				Catalog:   catalog,
+				Catalog:   *catalog,
 				Config:    cfg,
 				Generator: bootstrapGenerator,
 				Stdout:    cmd.OutOrStdout(),
@@ -137,3 +134,34 @@ func newRunCmd() *cobra.Command {
 	cmd.PersistentFlags().StringVar(&cfg.DataplaneRuntime.TokenPath, "dataplane-token-file", cfg.DataplaneRuntime.TokenPath, "Path to a file with dataplane token (use 'kumactl generate dataplane-token' to get one)")
 	return cmd
 }
+
+// fetchCatalog tries to fetch Kuma CP catalog several times
+// The main reason for introducing retries here is situation when DP is deployed in the same time as CP (ex. Ingress for Remote CP)
+func fetchCatalog(cfg kuma_dp.Config) (*catalog.Catalog, error) {
+	runLog.Info("connecting to the Control Plane API for Bootstrap API location")
+	catalogClient, err := catalogClientFactory(cfg.ControlPlane.ApiServer.URL)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not create catalog client")
+	}
+
+	backoff, err := retry.NewConstant(cfg.ControlPlane.ApiServer.Retry.Backoff)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not create retry backoff")
+	}
+	backoff = retry.WithMaxDuration(cfg.ControlPlane.ApiServer.Retry.MaxDuration, backoff)
+	var c catalog.Catalog
+	err = retry.Do(context.Background(), backoff, func(ctx context.Context) error {
+		c, err = catalogClient.Catalog()
+		if err != nil {
+			runLog.Info("could not connect to the Control Plane API. Retrying.", "backoff", cfg.ControlPlane.ApiServer.Retry.Backoff, "err", err.Error())
+			return retry.RetryableError(err)
+		}
+		return nil
+	})
+
+	if err != nil {
+		return nil, errors.Wrap(err, "could not retrieve catalog")
+	}
+	runLog.Info("connection successful", "catalog", c)
+	return &c, nil
+}
diff --git a/app/kuma-dp/pkg/dataplane/envoy/envoy.go b/app/kuma-dp/pkg/dataplane/envoy/envoy.go
index 0c4172652..d3b419f07 100644
--- a/app/kuma-dp/pkg/dataplane/envoy/envoy.go
+++ b/app/kuma-dp/pkg/dataplane/envoy/envoy.go
@@ -100,6 +100,7 @@ func lookupEnvoyPath(configuredPath string) (string, error) {
 }
 
 func (e *Envoy) Start(stop <-chan struct{}) error {
+	runLog.Info("generating bootstrap configuration")
 	bootstrapConfig, err := e.opts.Generator(e.opts.Catalog.Apis.Bootstrap.Url, e.opts.Config)
 	if err != nil {
 		return errors.Errorf("Failed to generate Envoy bootstrap config. %v", err)
@@ -108,6 +109,7 @@ func (e *Envoy) Start(stop <-chan struct{}) error {
 	if err != nil {
 		return err
 	}
+	runLog.Info("bootstrap configuration saved to a file", "file", configFile)
 
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
diff --git a/app/kuma-dp/pkg/dataplane/envoy/remote_bootstrap.go b/app/kuma-dp/pkg/dataplane/envoy/remote_bootstrap.go
index 7366130c1..9bf01aff0 100644
--- a/app/kuma-dp/pkg/dataplane/envoy/remote_bootstrap.go
+++ b/app/kuma-dp/pkg/dataplane/envoy/remote_bootstrap.go
@@ -2,6 +2,7 @@ package envoy
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"io/ioutil"
 	"net/http"
@@ -10,8 +11,10 @@ import (
 	envoy_bootstrap "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v2"
 	"github.com/golang/protobuf/proto"
 	"github.com/pkg/errors"
+	"github.com/sethvargo/go-retry"
 
 	kuma_dp "github.com/kumahq/kuma/pkg/config/app/kuma-dp"
+	"github.com/kumahq/kuma/pkg/core"
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
 	"github.com/kumahq/kuma/pkg/xds/bootstrap/types"
 )
@@ -25,12 +28,51 @@ func NewRemoteBootstrapGenerator(client *http.Client) BootstrapConfigFactoryFunc
 	return rb.Generate
 }
 
+var (
+	log           = core.Log.WithName("dataplane")
+	DpNotFoundErr = errors.New("Dataplane entity not found. If you are running on Universal please create a Dataplane entity on kuma-cp before starting kuma-dp. If you are running on Kubernetes, please check the kuma-cp logs to determine why the Dataplane entity could not be created by the automatic sidecar injection.")
+)
+
 func (b *remoteBootstrap) Generate(url string, cfg kuma_dp.Config) (proto.Message, error) {
 	bootstrapUrl, err := net_url.Parse(url)
 	if err != nil {
 		return nil, err
 	}
-	bootstrapUrl.Path = "/bootstrap"
+
+	backoff, err := retry.NewConstant(cfg.ControlPlane.BootstrapServer.Retry.Backoff)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not create retry backoff")
+	}
+	backoff = retry.WithMaxDuration(cfg.ControlPlane.BootstrapServer.Retry.MaxDuration, backoff)
+	var respBytes []byte
+	err = retry.Do(context.Background(), backoff, func(ctx context.Context) error {
+		log.Info("trying to fetch bootstrap configuration from the Control Plane")
+		respBytes, err = b.requestForBootstrap(bootstrapUrl, cfg)
+		if err == nil {
+			return nil
+		}
+		switch err {
+		case DpNotFoundErr:
+			log.Info("Dataplane entity is not yet found in the Control Plane. If you are running on Kubernetes, CP is most likely still in the process of converting Pod to Dataplane. Retrying.", "backoff", cfg.ControlPlane.ApiServer.Retry.Backoff)
+		default:
+			log.Info("could not fetch bootstrap configuration. Retrying.", "backoff", cfg.ControlPlane.BootstrapServer.Retry.Backoff, "err", err.Error())
+		}
+		return retry.RetryableError(err)
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	bootstrap := envoy_bootstrap.Bootstrap{}
+	if err := util_proto.FromYAML(respBytes, &bootstrap); err != nil {
+		return nil, errors.Wrap(err, "could not parse the bootstrap configuration")
+	}
+
+	return &bootstrap, nil
+}
+
+func (b *remoteBootstrap) requestForBootstrap(url *net_url.URL, cfg kuma_dp.Config) ([]byte, error) {
+	url.Path = "/bootstrap"
 	request := types.BootstrapRequest{
 		Mesh: cfg.Dataplane.Mesh,
 		Name: cfg.Dataplane.Name,
@@ -43,14 +85,14 @@ func (b *remoteBootstrap) Generate(url string, cfg kuma_dp.Config) (proto.Messag
 	if err != nil {
 		return nil, errors.Wrap(err, "could not marshal request to json")
 	}
-	resp, err := b.client.Post(bootstrapUrl.String(), "application/json", bytes.NewReader(jsonBytes))
+	resp, err := b.client.Post(url.String(), "application/json", bytes.NewReader(jsonBytes))
 	if err != nil {
 		return nil, errors.Wrap(err, "request to bootstrap server failed")
 	}
 	defer resp.Body.Close()
 	if resp.StatusCode != http.StatusOK {
 		if resp.StatusCode == http.StatusNotFound {
-			return nil, errors.New("Dataplane entity not found. If you are running on Universal please create a Dataplane entity on kuma-cp before starting kuma-dp. If you are running on Kubernetes, please check the kuma-cp logs to determine why the Dataplane entity could not be created by the automatic sidecar injection.")
+			return nil, DpNotFoundErr
 		}
 		if resp.StatusCode == http.StatusUnprocessableEntity {
 			bodyBytes, err := ioutil.ReadAll(resp.Body)
@@ -61,15 +103,9 @@ func (b *remoteBootstrap) Generate(url string, cfg kuma_dp.Config) (proto.Messag
 		}
 		return nil, errors.Errorf("unexpected status code: %d", resp.StatusCode)
 	}
-
-	bootstrap := envoy_bootstrap.Bootstrap{}
 	respBytes, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
 		return nil, errors.Wrap(err, "could not read the body of the response")
 	}
-	if err := util_proto.FromYAML(respBytes, &bootstrap); err != nil {
-		return nil, errors.Wrap(err, "could not parse the bootstrap configuration")
-	}
-
-	return &bootstrap, nil
+	return respBytes, nil
 }
diff --git a/deployments/charts/kuma/Chart.yaml b/deployments/charts/kuma/Chart.yaml
index 0cb53a3b1..64e4e46fe 100644
--- a/deployments/charts/kuma/Chart.yaml
+++ b/deployments/charts/kuma/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
 name: kuma
 description: A Helm chart for the Kuma Control Plane
 type: application
-version: 0.2.1
-appVersion: 0.7.1
+version: 0.2.2
+appVersion: 0.7.2
 home: https://github.com/kumahq/kuma
 icon: https://kuma.io/images/brand/kuma-logo-new.svg
 keywords:
diff --git a/mk/dev.mk b/mk/dev.mk
index 749c49856..c629d1cbc 100644
--- a/mk/dev.mk
+++ b/mk/dev.mk
@@ -182,10 +182,11 @@ dev/install/goimports: ## Bootstrap: Install goimports
 dev/install/helm3: ## Bootstrap: Install Helm 3
 	curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
 
+GEN_CHANGELOG_START_TAG ?= 0.7.2
+GEN_CHANGELOG_BRANCH ?= master
 GEN_CHANGELOG_MD ?= changelog.generated.md
-GEN_CHANGELOG_START_TAG ?= 0.6.0
 .PHONY: changelog
 changelog:
 	@cd tools/releases/changelog/ && \
-	go run ./... --start $(GEN_CHANGELOG_START_TAG) > ../../../$(GEN_CHANGELOG_MD)
+	go run ./... --branch refs/heads/$(GEN_CHANGELOG_BRANCH) --start refs/heads/$(GEN_CHANGELOG_START_TAG) > ../../../$(GEN_CHANGELOG_MD)
 	@echo "The generated changelog is in $(GEN_CHANGELOG_MD)"
diff --git a/pkg/config/app/kuma-dp/config.go b/pkg/config/app/kuma-dp/config.go
index 0051c44dc..822a9a88b 100644
--- a/pkg/config/app/kuma-dp/config.go
+++ b/pkg/config/app/kuma-dp/config.go
@@ -16,6 +16,16 @@ func DefaultConfig() Config {
 		ControlPlane: ControlPlane{
 			ApiServer: ApiServer{
 				URL: "http://localhost:5681",
+				Retry: ApiServerRetry{
+					Backoff:     3 * time.Second,
+					MaxDuration: 5 * time.Minute, // this value can be fairy long since what will happen when there there is a connection error is that the Dataplane will be restarted (by process manager like systemd/K8S etc.) and will try to connect again.
+				},
+			},
+			BootstrapServer: BootstrapServer{
+				Retry: BootstrapServerRetry{
+					Backoff:     1 * time.Second,
+					MaxDuration: 30 * time.Second, // CP should be able to create Dataplen definition in this time, if it hasn't there is probably an error with Pod -> Dataplane conversion.
+				},
 			},
 		},
 		Dataplane: Dataplane{
@@ -51,13 +61,77 @@ func (c *Config) Sanitize() {
 type ControlPlane struct {
 	// ApiServer defines coordinates of the Control Plane API Server
 	ApiServer ApiServer `yaml:"apiServer,omitempty"`
+	// BootstrapServer defines settings of the Control Plane Bootstrap Server
+	BootstrapServer BootstrapServer `yaml:"bootstrapServer,omitempty"`
 }
 
 type ApiServer struct {
 	// Address defines the address of Control Plane API server.
 	URL string `yaml:"url,omitempty" envconfig:"kuma_control_plane_api_server_url"`
+	// Retry settings for API Server
+	Retry ApiServerRetry `yaml:"retry,omitempty"`
+}
+
+type ApiServerRetry struct {
+	// Duration to wait between retries
+	Backoff time.Duration `yaml:"backoff,omitempty" envconfig:"kuma_control_plane_api_server_retry_backoff"`
+	// Max duration for retries (this is not exact time for execution, the check is done between retries)
+	MaxDuration time.Duration `yaml:"maxDuration,omitempty" envconfig:"kuma_control_plane_api_server_retry_max_duration"`
+}
+
+func (a *ApiServerRetry) Sanitize() {
 }
 
+func (a *ApiServerRetry) Validate() error {
+	if a.Backoff <= 0 {
+		return errors.New(".Backoff must be a positive duration")
+	}
+	if a.MaxDuration <= 0 {
+		return errors.New(".MaxDuration must be a positive duration")
+	}
+	return nil
+}
+
+var _ config.Config = &ApiServerRetry{}
+
+type BootstrapServer struct {
+	Retry BootstrapServerRetry `yaml:"retry,omitempty"`
+}
+
+func (b *BootstrapServer) Sanitize() {
+}
+
+func (b *BootstrapServer) Validate() error {
+	if err := b.Retry.Validate(); err != nil {
+		return errors.Wrap(err, ".Retry is not valid")
+	}
+	return nil
+}
+
+var _ config.Config = &BootstrapServer{}
+
+type BootstrapServerRetry struct {
+	// Duration to wait between retries
+	Backoff time.Duration `yaml:"backoff,omitempty" envconfig:"kuma_control_plane_bootstrap_server_retry_backoff"`
+	// Max duration for retries (this is not exact time for execution, the check is done between retries)
+	MaxDuration time.Duration `yaml:"maxDuration,omitempty" envconfig:"kuma_control_plane_bootstrap_server_retry_max_duration"`
+}
+
+func (b *BootstrapServerRetry) Sanitize() {
+}
+
+func (b *BootstrapServerRetry) Validate() error {
+	if b.Backoff <= 0 {
+		return errors.New(".Backoff must be a positive duration")
+	}
+	if b.MaxDuration <= 0 {
+		return errors.New(".MaxDuration must be a positive duration")
+	}
+	return nil
+}
+
+var _ config.Config = &BootstrapServerRetry{}
+
 // Dataplane defines bootstrap configuration of the dataplane (Envoy).
 type Dataplane struct {
 	// Mesh name.
@@ -107,6 +181,9 @@ func (c *ControlPlane) Validate() (errs error) {
 	if err := c.ApiServer.Validate(); err != nil {
 		errs = multierr.Append(errs, errors.Wrapf(err, ".ApiServer is not valid"))
 	}
+	if err := c.BootstrapServer.Validate(); err != nil {
+		errs = multierr.Append(errs, errors.Wrapf(err, ".BootstrapServer is not valid"))
+	}
 	return
 }
 
@@ -155,5 +232,8 @@ func (d *ApiServer) Validate() (errs error) {
 	} else if !url.IsAbs() {
 		errs = multierr.Append(errs, errors.Errorf(".URL must be a valid absolute URI"))
 	}
+	if err := d.Retry.Validate(); err != nil {
+		errs = multierr.Append(errs, errors.Wrap(err, ".Retry is not valid"))
+	}
 	return
 }
diff --git a/pkg/config/util.go b/pkg/config/util.go
index a73a88a8f..573393676 100644
--- a/pkg/config/util.go
+++ b/pkg/config/util.go
@@ -13,6 +13,9 @@ func ToYAML(cfg Config) ([]byte, error) {
 	return yaml.Marshal(cfg)
 }
 
+// ToJson converts through YAML, because we only have `yaml` tags on Config.
+// This JSON cannot be parsed by json.Unmarshal because durations are marshaled by yaml to pretty form like "1s".
+// To change it to simple json.Marshal we need to add `json` tag everywhere.
 func ToJson(cfg Config) ([]byte, error) {
 	yamlBytes, err := ToYAML(cfg)
 	if err != nil {
diff --git a/pkg/core/runtime/component/component.go b/pkg/core/runtime/component/component.go
index a2bb28f2d..903e3a253 100644
--- a/pkg/core/runtime/component/component.go
+++ b/pkg/core/runtime/component/component.go
@@ -122,7 +122,6 @@ func (cm *manager) startLeaderComponents(stop <-chan struct{}, errCh chan error)
 			closeLeaderCh()
 		},
 	})
-	log.Info("Starting leader election")
 	go cm.leaderElector.Start(stop)
 	go func() {
 		<-stop
diff --git a/pkg/plugins/leader/postgres/leader_elector.go b/pkg/plugins/leader/postgres/leader_elector.go
index fa6afcd80..cd5656ec2 100644
--- a/pkg/plugins/leader/postgres/leader_elector.go
+++ b/pkg/plugins/leader/postgres/leader_elector.go
@@ -39,6 +39,7 @@ func NewPostgresLeaderElector(lockClient *pglock.Client) component.LeaderElector
 }
 
 func (p *postgresLeaderElector) Start(stop <-chan struct{}) {
+	log.Info("Starting leader election")
 	ctx, cancelFn := context.WithCancel(context.Background())
 	go func() {
 		<-stop
diff --git a/tools/releases/changelog/go.mod b/tools/releases/changelog/go.mod
index f7f40bf5d..a75cf3e13 100644
--- a/tools/releases/changelog/go.mod
+++ b/tools/releases/changelog/go.mod
@@ -3,8 +3,9 @@ module github.com/kumahq/kuma/tools/releases/changelog
 go 1.14
 
 require (
-	github.com/go-git/go-git/v5 v5.0.0
-	github.com/google/go-github/v32 v32.0.0
+	github.com/go-git/go-git/v5 v5.1.0
+	github.com/google/go-github/v32 v32.1.0
 	github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0
+	github.com/prometheus/common v0.4.0
 	github.com/spf13/cobra v1.0.0
 )
diff --git a/tools/releases/changelog/go.sum b/tools/releases/changelog/go.sum
index 52d4eea2d..324ab02d3 100644
--- a/tools/releases/changelog/go.sum
+++ b/tools/releases/changelog/go.sum
@@ -3,7 +3,9 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
@@ -40,8 +42,8 @@ github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agR
 github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
 github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc=
 github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
-github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
-github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
+github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk=
+github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
@@ -58,8 +60,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-github/v32 v32.0.0 h1:q74KVb22spUq0U5HqZ9VCYqQz8YRuOtL/39ZnfwO+NM=
-github.com/google/go-github/v32 v32.0.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
+github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
+github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
 github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -69,6 +71,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk=
 github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
+github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
+github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@@ -107,6 +111,7 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -116,6 +121,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@@ -183,6 +189,7 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/tools/releases/changelog/root.go b/tools/releases/changelog/root.go
index aa4bf614a..7a852fcac 100644
--- a/tools/releases/changelog/root.go
+++ b/tools/releases/changelog/root.go
@@ -14,6 +14,7 @@ import (
 
 var config struct {
 	gitHubRepo string
+	branch     string
 	startTag   string
 	endTag     string
 }
@@ -30,9 +31,11 @@ var rootCmd = &cobra.Command{
 	Run: func(cmd *cobra.Command, args []string) {
 		// Clones the given repository, creating the remote, the local branches
 		// and fetching the objects, everything in memory:
-		Info("Clone %s", config.gitHubRepo)
+		Info("Clone %s branch %s", config.gitHubRepo, plumbing.ReferenceName(config.branch))
 		r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{
-			URL: config.gitHubRepo,
+			URL:           config.gitHubRepo,
+			ReferenceName: plumbing.ReferenceName(config.branch),
+			SingleBranch:  false,
 		})
 		CheckIfError(err)
 
@@ -49,12 +52,22 @@ var rootCmd = &cobra.Command{
 
 		Info("Retrieve commit history")
 		// ... retrieves the commit history
-		cIter, err := r.Log(&git.LogOptions{})
+		refHash := plumbing.Hash{}
+		startRef, err := r.Reference(plumbing.ReferenceName(config.startTag), false)
+		if err != nil {
+			Info("error", err)
+		} else {
+			refHash = startRef.Hash()
+		}
+		cIter, err := r.Log(&git.LogOptions{
+			From:  refHash,
+			Order: git.LogOrderCommitterTime,
+		})
 		CheckIfError(err)
 
 		Info("Filter the commits by tag")
 		generator := NewGenerator(config.startTag, config.endTag)
-		currentTag := "master"
+		currentTag := config.branch
 		err = cIter.ForEach(func(c *object.Commit) error {
 			if tag, found := tagMap[c.Hash.String()]; found {
 				currentTag = tag
@@ -81,6 +94,7 @@ func Execute() {
 
 func init() {
 	rootCmd.Flags().StringVar(&config.gitHubRepo, "repo", "https://github.com/kumahq/kuma.git", "The GitHub repo to process")
+	rootCmd.Flags().StringVar(&config.branch, "branch", "master", "The branch to process")
 	rootCmd.Flags().StringVar(&config.startTag, "start", "", "The start hash or tag")
 	rootCmd.Flags().StringVar(&config.endTag, "end", "", "The end hash or tag")
 }


Test patch:
diff --git a/app/kuma-dp/pkg/dataplane/envoy/remote_bootstrap_test.go b/app/kuma-dp/pkg/dataplane/envoy/remote_bootstrap_test.go
index b7b499a35..84d9c5fb2 100644
--- a/app/kuma-dp/pkg/dataplane/envoy/remote_bootstrap_test.go
+++ b/app/kuma-dp/pkg/dataplane/envoy/remote_bootstrap_test.go
@@ -8,6 +8,7 @@ import (
 	"path/filepath"
 	"strconv"
 	"strings"
+	"time"
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/ginkgo/extensions/table"
@@ -115,6 +116,40 @@ var _ = Describe("Remote Bootstrap", func() {
 			}()),
 	)
 
+	It("should retry when DP is not found", func() {
+		// given
+		mux := http.NewServeMux()
+		server := httptest.NewServer(mux)
+		defer server.Close()
+		i := 0
+		mux.HandleFunc("/bootstrap", func(writer http.ResponseWriter, req *http.Request) {
+			defer GinkgoRecover()
+			if i < 2 {
+				writer.WriteHeader(404)
+				i++
+			} else {
+				response, err := ioutil.ReadFile(filepath.Join("testdata", "remote-bootstrap-config.golden.yaml"))
+				Expect(err).ToNot(HaveOccurred())
+				_, err = writer.Write(response)
+				Expect(err).ToNot(HaveOccurred())
+			}
+		})
+		port, err := strconv.Atoi(strings.Split(server.Listener.Addr().String(), ":")[1])
+		Expect(err).ToNot(HaveOccurred())
+
+		// and
+		generator := NewRemoteBootstrapGenerator(http.DefaultClient)
+
+		// when
+		cfg := kuma_dp.DefaultConfig()
+		cfg.ControlPlane.BootstrapServer.Retry.Backoff = 10 * time.Millisecond
+		_, err = generator(fmt.Sprintf("http://localhost:%d", port), cfg)
+
+		// then
+		Expect(err).ToNot(HaveOccurred())
+		Expect(cfg).ToNot(BeNil())
+	})
+
 	It("should return error when DP is not found", func() {
 		// given
 		mux := http.NewServeMux()
@@ -131,9 +166,12 @@ var _ = Describe("Remote Bootstrap", func() {
 		generator := NewRemoteBootstrapGenerator(http.DefaultClient)
 
 		// when
-		_, err = generator(fmt.Sprintf("http://localhost:%d", port), kuma_dp.DefaultConfig())
+		config := kuma_dp.DefaultConfig()
+		config.ControlPlane.BootstrapServer.Retry.Backoff = 10 * time.Millisecond
+		config.ControlPlane.BootstrapServer.Retry.MaxDuration = 100 * time.Millisecond
+		_, err = generator(fmt.Sprintf("http://localhost:%d", port), config)
 
 		// then
-		Expect(err).To(MatchError("Dataplane entity not found. If you are running on Universal please create a Dataplane entity on kuma-cp before starting kuma-dp. If you are running on Kubernetes, please check the kuma-cp logs to determine why the Dataplane entity could not be created by the automatic sidecar injection."))
+		Expect(err).To(MatchError("retryable: Dataplane entity not found. If you are running on Universal please create a Dataplane entity on kuma-cp before starting kuma-dp. If you are running on Kubernetes, please check the kuma-cp logs to determine why the Dataplane entity could not be created by the automatic sidecar injection."))
 	})
 })
diff --git a/pkg/config/app/kuma-dp/config_test.go b/pkg/config/app/kuma-dp/config_test.go
index 1d799cc09..0b9ed5a59 100644
--- a/pkg/config/app/kuma-dp/config_test.go
+++ b/pkg/config/app/kuma-dp/config_test.go
@@ -51,14 +51,18 @@ var _ = Describe("Config", func() {
 		It("should be loadable from environment variables", func() {
 			// setup
 			env := map[string]string{
-				"KUMA_CONTROL_PLANE_API_SERVER_URL":  "https://kuma-control-plane.internal:5682",
-				"KUMA_DATAPLANE_MESH":                "demo",
-				"KUMA_DATAPLANE_NAME":                "example",
-				"KUMA_DATAPLANE_ADMIN_PORT":          "2345",
-				"KUMA_DATAPLANE_DRAIN_TIME":          "60s",
-				"KUMA_DATAPLANE_RUNTIME_BINARY_PATH": "envoy.sh",
-				"KUMA_DATAPLANE_RUNTIME_CONFIG_DIR":  "/var/run/envoy",
-				"KUMA_DATAPLANE_RUNTIME_TOKEN_PATH":  "/tmp/token",
+				"KUMA_CONTROL_PLANE_API_SERVER_URL":                      "https://kuma-control-plane.internal:5682",
+				"KUMA_CONTROL_PLANE_API_SERVER_RETRY_BACKOFF":            "1s",
+				"KUMA_CONTROL_PLANE_API_SERVER_RETRY_MAX_DURATION":       "10s",
+				"KUMA_CONTROL_PLANE_BOOTSTRAP_SERVER_RETRY_BACKOFF":      "2s",
+				"KUMA_CONTROL_PLANE_BOOTSTRAP_SERVER_RETRY_MAX_DURATION": "11s",
+				"KUMA_DATAPLANE_MESH":                                    "demo",
+				"KUMA_DATAPLANE_NAME":                                    "example",
+				"KUMA_DATAPLANE_ADMIN_PORT":                              "2345",
+				"KUMA_DATAPLANE_DRAIN_TIME":                              "60s",
+				"KUMA_DATAPLANE_RUNTIME_BINARY_PATH":                     "envoy.sh",
+				"KUMA_DATAPLANE_RUNTIME_CONFIG_DIR":                      "/var/run/envoy",
+				"KUMA_DATAPLANE_RUNTIME_TOKEN_PATH":                      "/tmp/token",
 			}
 			for key, value := range env {
 				os.Setenv(key, value)
@@ -75,6 +79,10 @@ var _ = Describe("Config", func() {
 
 			// and
 			Expect(cfg.ControlPlane.ApiServer.URL).To(Equal("https://kuma-control-plane.internal:5682"))
+			Expect(cfg.ControlPlane.ApiServer.Retry.Backoff).To(Equal(1 * time.Second))
+			Expect(cfg.ControlPlane.ApiServer.Retry.MaxDuration).To(Equal(10 * time.Second))
+			Expect(cfg.ControlPlane.BootstrapServer.Retry.Backoff).To(Equal(2 * time.Second))
+			Expect(cfg.ControlPlane.BootstrapServer.Retry.MaxDuration).To(Equal(11 * time.Second))
 			Expect(cfg.Dataplane.Mesh).To(Equal("demo"))
 			Expect(cfg.Dataplane.Name).To(Equal("example"))
 			Expect(cfg.Dataplane.AdminPort).To(Equal(config_types.MustExactPort(2345)))
@@ -110,6 +118,6 @@ var _ = Describe("Config", func() {
 		err := config.Load(filepath.Join("testdata", "invalid-config.input.yaml"), &cfg)
 
 		// then
-		Expect(err).To(MatchError(`Invalid configuration: .ControlPlane is not valid: .ApiServer is not valid: .URL must be a valid absolute URI; .Dataplane is not valid: .Mesh must be non-empty; .Name must be non-empty; .DrainTime must be positive; .DataplaneRuntime is not valid: .BinaryPath must be non-empty`))
+		Expect(err.Error()).To(Equal(`Invalid configuration: .ControlPlane is not valid: .ApiServer is not valid: .URL must be a valid absolute URI; .Retry is not valid: .Backoff must be a positive duration; .BootstrapServer is not valid: .Retry is not valid: .Backoff must be a positive duration; .Dataplane is not valid: .Mesh must be non-empty; .Name must be non-empty; .DrainTime must be positive; .DataplaneRuntime is not valid: .BinaryPath must be non-empty`))
 	})
 })
diff --git a/pkg/config/app/kuma-dp/testdata/default-config.golden.yaml b/pkg/config/app/kuma-dp/testdata/default-config.golden.yaml
index 8acea8e0e..f1c73110d 100644
--- a/pkg/config/app/kuma-dp/testdata/default-config.golden.yaml
+++ b/pkg/config/app/kuma-dp/testdata/default-config.golden.yaml
@@ -1,6 +1,13 @@
 controlPlane:
   apiServer:
     url: http://localhost:5681
+    retry:
+      backoff: 3s
+      maxDuration: 5m0s
+  bootstrapServer:
+    retry:
+      backoff: 1s
+      maxDuration: 30s
 dataplane:
   mesh: default
   drainTime: 30s
diff --git a/pkg/config/app/kuma-dp/testdata/invalid-config.input.yaml b/pkg/config/app/kuma-dp/testdata/invalid-config.input.yaml
index 24e001f41..568d1b1ef 100644
--- a/pkg/config/app/kuma-dp/testdata/invalid-config.input.yaml
+++ b/pkg/config/app/kuma-dp/testdata/invalid-config.input.yaml
@@ -1,6 +1,13 @@
 controlPlane:
   apiServer:
     url: invalid-url
+    retry:
+      backoff: 0s
+      maxDuration: 0s
+  boostrapServer:
+    retry:
+      backoff: 0s
+      maxDuration: 0s
 dataplane:
   mesh:
   name:
diff --git a/pkg/config/app/kuma-dp/testdata/valid-config.input.yaml b/pkg/config/app/kuma-dp/testdata/valid-config.input.yaml
index 159c3ba94..32839d331 100644
--- a/pkg/config/app/kuma-dp/testdata/valid-config.input.yaml
+++ b/pkg/config/app/kuma-dp/testdata/valid-config.input.yaml
@@ -1,6 +1,13 @@
 controlPlane:
   apiServer:
     url: https://kuma-control-plane.internal:5682
+    retry:
+      backoff: 1s
+      maxDuration: 10s
+  bootstrapServer:
+    retry:
+      backoff: 2s
+      maxDuration: 11s
 dataplane:
   mesh: demo
   name: example