mirror of https://github.com/dapr/cli.git
Add dapr upgrade command (#566)
* add dapr upgrade command * make flags required and update README * linter * remove hard-coded namespace * add unit tests * remove default runtime-version for upgrade * seperate ha from mtls * allow upgrading helm installations
This commit is contained in:
parent
790b349e43
commit
6d90ec1c77
14
README.md
14
README.md
|
@ -173,7 +173,7 @@ $ dapr uninstall --network dapr-network
|
|||
|
||||
The init command will install Dapr to a Kubernetes cluster. For more advanced use cases, use our [Helm Chart](https://github.com/dapr/dapr/tree/master/charts/dapr).
|
||||
|
||||
*Note: The default namespace is dapr-system*
|
||||
*Note: The default namespace is dapr-system. The installation will appear under the name `dapr` for Helm*
|
||||
|
||||
```
|
||||
$ dapr init -k
|
||||
|
@ -211,6 +211,18 @@ To remove Dapr from your Kubernetes cluster, use the `uninstall` command with `-
|
|||
$ dapr uninstall -k
|
||||
```
|
||||
|
||||
### Upgrade Dapr on Kubernetes
|
||||
|
||||
To perform a zero downtime upgrade of the Dapr control plane:
|
||||
|
||||
```
|
||||
$ dapr upgrade -k --runtime-version=1.0.0-rc.2
|
||||
```
|
||||
|
||||
The example above shows how to upgrade from your current version to version `1.0.0-rc.2`.
|
||||
|
||||
*Note: do not use the `dapr upgrade` command if you're upgrading from 0.x versions of Dapr*
|
||||
|
||||
### Launch Dapr and your app
|
||||
|
||||
The Dapr CLI lets you debug easily by launching both Dapr and your app.
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/dapr/cli/pkg/kubernetes"
|
||||
"github.com/dapr/cli/pkg/print"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var UpgradeCmd = &cobra.Command{
|
||||
Use: "upgrade",
|
||||
Short: "Upgrades a Dapr control plane installation in a cluster. Supported platforms: Kubernetes",
|
||||
Example: `
|
||||
# Upgrade Dapr in Kubernetes
|
||||
dapr upgrade -k
|
||||
|
||||
# See more at: https://docs.dapr.io/getting-started/
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := kubernetes.Upgrade(kubernetes.UpgradeConfig{
|
||||
RuntimeVersion: runtimeVersion,
|
||||
})
|
||||
if err != nil {
|
||||
print.FailureStatusEvent(os.Stdout, "Failed to upgrade Dapr: %s", err)
|
||||
return
|
||||
}
|
||||
print.SuccessStatusEvent(os.Stdout, "Dapr control plane successfully upgraded to version %s. Make sure your deployments are restarted to pick up the latest sidecar version.", runtimeVersion)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
UpgradeCmd.Flags().BoolVarP(&kubernetesMode, "kubernetes", "k", false, "Upgrade Dapr in a Kubernetes cluster")
|
||||
UpgradeCmd.Flags().StringVarP(&runtimeVersion, "runtime-version", "", "", "The version of the Dapr runtime to upgrade to, for example: 1.0.0")
|
||||
UpgradeCmd.Flags().BoolP("help", "h", false, "Print this help message")
|
||||
|
||||
UpgradeCmd.MarkFlagRequired("runtime-version")
|
||||
UpgradeCmd.MarkFlagRequired("kubernetes")
|
||||
|
||||
RootCmd.AddCommand(UpgradeCmd)
|
||||
}
|
1
go.mod
1
go.mod
|
@ -26,6 +26,7 @@ require (
|
|||
gopkg.in/yaml.v2 v2.3.0
|
||||
helm.sh/helm/v3 v3.4.0
|
||||
k8s.io/api v0.20.0
|
||||
k8s.io/apiextensions-apiserver v0.20.0
|
||||
k8s.io/apimachinery v0.20.0
|
||||
k8s.io/cli-runtime v0.20.0
|
||||
k8s.io/client-go v0.20.0
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/dapr/cli/pkg/print"
|
||||
"github.com/dapr/cli/utils"
|
||||
helm "helm.sh/helm/v3/pkg/action"
|
||||
"k8s.io/helm/pkg/strvals"
|
||||
)
|
||||
|
||||
const operatorName = "dapr-operator"
|
||||
|
||||
var crds = []string{
|
||||
"components",
|
||||
"configuration",
|
||||
"subscription",
|
||||
}
|
||||
|
||||
type UpgradeConfig struct {
|
||||
RuntimeVersion string
|
||||
}
|
||||
|
||||
func Upgrade(conf UpgradeConfig) error {
|
||||
sc, err := NewStatusClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
status, err := sc.Status()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(status) == 0 {
|
||||
return errors.New("dapr is not installed in your cluster")
|
||||
}
|
||||
|
||||
var daprVersion string
|
||||
for _, s := range status {
|
||||
if s.Name == operatorName {
|
||||
daprVersion = s.Version
|
||||
}
|
||||
}
|
||||
print.InfoStatusEvent(os.Stdout, "Dapr control plane version %s detected in namespace %s", daprVersion, status[0].Namespace)
|
||||
|
||||
helmConf, err := helmConfig(status[0].Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
daprChart, err := daprChart(conf.RuntimeVersion, helmConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upgradeClient := helm.NewUpgrade(helmConf)
|
||||
upgradeClient.ResetValues = true
|
||||
upgradeClient.Namespace = status[0].Namespace
|
||||
upgradeClient.CleanupOnFail = true
|
||||
upgradeClient.Wait = true
|
||||
|
||||
print.InfoStatusEvent(os.Stdout, "Starting upgrade...")
|
||||
|
||||
mtls, err := IsMTLSEnabled()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var vals map[string]interface{}
|
||||
var ca []byte
|
||||
var issuerCert []byte
|
||||
var issuerKey []byte
|
||||
|
||||
if mtls {
|
||||
secret, sErr := getTrustChainSecret()
|
||||
if sErr != nil {
|
||||
return sErr
|
||||
}
|
||||
|
||||
ca = secret.Data["ca.crt"]
|
||||
issuerCert = secret.Data["issuer.crt"]
|
||||
issuerKey = secret.Data["issuer.key"]
|
||||
}
|
||||
|
||||
ha := highAvailabilityEnabled(status)
|
||||
vals, err = upgradeChartValues(string(ca), string(issuerCert), string(issuerKey), ha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = applyCRDs(fmt.Sprintf("v%s", conf.RuntimeVersion))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listClient := helm.NewList(helmConf)
|
||||
releases, err := listClient.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var chart string
|
||||
for _, r := range releases {
|
||||
if r.Chart != nil && strings.Contains(r.Chart.Name(), "dapr") {
|
||||
chart = r.Name
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = upgradeClient.Run(chart, daprChart, vals); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func highAvailabilityEnabled(status []StatusOutput) bool {
|
||||
for _, s := range status {
|
||||
if s.Replicas > 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func applyCRDs(version string) error {
|
||||
for _, crd := range crds {
|
||||
url := fmt.Sprintf("https://raw.githubusercontent.com/dapr/dapr/%s/charts/dapr/crds/%s.yaml", version, crd)
|
||||
_, err := utils.RunCmdAndWait("kubectl", "apply", "-f", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func upgradeChartValues(ca, issuerCert, issuerKey string, haMode bool) (map[string]interface{}, error) {
|
||||
chartVals := map[string]interface{}{}
|
||||
globalVals := []string{}
|
||||
|
||||
if ca != "" && issuerCert != "" && issuerKey != "" {
|
||||
globalVals = append(globalVals, fmt.Sprintf("dapr_sentry.tls.root.certPEM=%s", ca),
|
||||
fmt.Sprintf("dapr_sentry.tls.issuer.certPEM=%s", issuerCert),
|
||||
fmt.Sprintf("dapr_sentry.tls.issuer.keyPEM=%s", issuerKey),
|
||||
)
|
||||
}
|
||||
|
||||
if haMode {
|
||||
globalVals = append(globalVals, "global.ha.enabled=true")
|
||||
}
|
||||
|
||||
for _, v := range globalVals {
|
||||
if err := strvals.ParseInto(v, chartVals); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return chartVals, nil
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHAMode(t *testing.T) {
|
||||
t.Run("ha mode", func(t *testing.T) {
|
||||
s := []StatusOutput{
|
||||
{
|
||||
Replicas: 3,
|
||||
},
|
||||
}
|
||||
|
||||
r := highAvailabilityEnabled(s)
|
||||
assert.True(t, r)
|
||||
})
|
||||
|
||||
t.Run("non-ha mode", func(t *testing.T) {
|
||||
s := []StatusOutput{
|
||||
{
|
||||
Replicas: 1,
|
||||
},
|
||||
}
|
||||
|
||||
r := highAvailabilityEnabled(s)
|
||||
assert.False(t, r)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMTLSChartValues(t *testing.T) {
|
||||
val, err := upgradeChartValues("1", "2", "3", true)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, val, 2)
|
||||
}
|
Loading…
Reference in New Issue