Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ spec:
- "--agent-name={{ .Release.Name }}"
- "--agent-namespace={{ .Release.Namespace }}"
- "--base-image={{ include "teleport-kube-agent.baseImage" . }}"
- "--version-server={{ $updater.versionServer }}"
- "--version-server={{ tpl $updater.versionServer . }}"
- "--version-channel={{ $updater.releaseChannel }}"
{{- if .Values.updater.extraArgs }}
{{- toYaml .Values.updater.extraArgs | nindent 10 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ tests:
- contains:
path: spec.template.spec.containers[0].args
content: "--agent-namespace=my-namespace"
- it: defaults the updater version server to the proxy address
set:
proxyAddr: proxy.teleport.example.com:443
roles: "custom"
updater:
enabled: true
asserts:
- contains:
path: spec.template.spec.containers[0].args
content: "--version-server=https://proxy.teleport.example.com:443/v1/webapi/automaticupgrades/channel"
- it: sets the updater version server
values:
- ../.lint/updater.yaml
Expand Down
3 changes: 2 additions & 1 deletion examples/chart/teleport-kube-agent/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ updater:
# `updater.versionServer` is the URL of the version server the agent fetches
# the target version from. The complete version endpoint is built by
# concatenating `versionServer` and `releaseChannel`.
versionServer: "https://updates.releases.teleport.dev/v1/"
# This field supports gotemplate.
versionServer: "https://{{ .Values.proxyAddr }}/v1/webapi/automaticupgrades/channel"
# Release channel the agent subscribes to.
releaseChannel: "stable/cloud"
image: public.ecr.aws/gravitational/teleport-kube-agent-updater
Expand Down
189 changes: 189 additions & 0 deletions integration/proxy/automaticupgrades_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package proxy

import (
"context"
"crypto/tls"
"fmt"
"io"
"net/http"
"net/url"
"path/filepath"
"testing"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/teleport/lib/automaticupgrades"
"github.com/gravitational/teleport/lib/automaticupgrades/basichttp"
"github.com/gravitational/teleport/lib/automaticupgrades/constants"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/utils"
)

func createProxyWithChannels(t *testing.T, channels automaticupgrades.Channels) string {
features := proto.Features{}
require.NoError(t, channels.CheckAndSetDefaults(features))
testDir := t.TempDir()

cfg := helpers.InstanceConfig{
ClusterName: "root.example.com",
HostID: uuid.New().String(),
NodeName: helpers.Loopback,
Log: utils.NewLoggerForTests(),
}
cfg.Listeners = helpers.SingleProxyPortSetup(t, &cfg.Fds)
rc := helpers.NewInstance(t, cfg)

var err error
rcConf := servicecfg.MakeDefaultConfig()
rcConf.DataDir = filepath.Join(testDir, "data")
rcConf.Auth.Enabled = true
rcConf.Proxy.Enabled = true
rcConf.SSH.Enabled = false
rcConf.Proxy.DisableWebInterface = true
rcConf.Version = "v3"
rcConf.Proxy.AutomaticUpgradesChannels = channels

err = rc.CreateEx(t, nil, rcConf)
require.NoError(t, err)
err = rc.Start()
require.NoError(t, err)
t.Cleanup(func() {
assert.NoError(t, rc.StopAll())
})

return cfg.Listeners.Web
}

func TestVersionServer(t *testing.T) {
// Test setup
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

testVersion := "v12.2.6"
testVersionMajorTooHigh := "v99.1.3"

staticChannel := "static/ok"
staticHighChannel := "static/high"
staticNoVersionChannel := "static/none"
forwardChannel := "forward/ok"
forwardHighChannel := "forward/high"
forwardNoVersionChannel := "forward/none"
forwardPath := "/version-server/"

upstreamServer := basichttp.NewServerMock(forwardPath + constants.VersionPath)
upstreamServer.SetResponse(t, http.StatusOK, testVersion)
upstreamHighServer := basichttp.NewServerMock(forwardPath + constants.VersionPath)
upstreamHighServer.SetResponse(t, http.StatusOK, testVersionMajorTooHigh)
upstreamNoVersionServer := basichttp.NewServerMock(forwardPath + constants.VersionPath)
upstreamNoVersionServer.SetResponse(t, http.StatusOK, constants.NoVersion)

channels := automaticupgrades.Channels{
staticChannel: {
StaticVersion: testVersion,
},
staticHighChannel: {
StaticVersion: testVersionMajorTooHigh,
},
staticNoVersionChannel: {
StaticVersion: constants.NoVersion,
},
forwardChannel: {
ForwardURL: upstreamServer.Srv.URL + forwardPath,
},
forwardHighChannel: {
ForwardURL: upstreamHighServer.Srv.URL + forwardPath,
},
forwardNoVersionChannel: {
ForwardURL: upstreamNoVersionServer.Srv.URL + forwardPath,
},
}

proxyAddr := createProxyWithChannels(t, channels)

tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
httpClient := http.Client{Transport: tr}

// Test execution
tests := []struct {
name string
channel string
expectedResponse string
}{
{
name: "static version OK",
channel: staticChannel,
expectedResponse: testVersion,
},
{
name: "static version too high",
channel: staticHighChannel,
expectedResponse: teleport.Version,
},
{
name: "static version none",
channel: staticNoVersionChannel,
expectedResponse: constants.NoVersion,
},
{
name: "forward version OK",
channel: forwardChannel,
expectedResponse: testVersion,
},
{
name: "forward version too high",
channel: forwardHighChannel,
expectedResponse: teleport.Version,
},
{
name: "forward version none",
channel: forwardNoVersionChannel,
expectedResponse: constants.NoVersion,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
channelUrl, err := url.Parse(
fmt.Sprintf("https://%s/v1/webapi/automaticupgrades/channel/%s/version", proxyAddr, tt.channel),
)
require.NoError(t, err)

req, err := http.NewRequestWithContext(ctx, http.MethodGet, channelUrl.String(), nil)
require.NoError(t, err)
res, err := httpClient.Do(req)
require.NoError(t, err)
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
require.NoError(t, err)

require.Equal(t, http.StatusOK, res.StatusCode)
require.Equal(t, tt.expectedResponse, string(body))
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ func main() {
os.Exit(1)
}

ctrl.Log.Info("starting the updater", "url", versionServerURL.String())

if err := mgr.Start(ctx); err != nil {
ctrl.Log.Error(err, "failed to start manager, exiting")
os.Exit(1)
Expand Down
4 changes: 4 additions & 0 deletions integrations/kube-agent-updater/pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ const (
VersionPath = "version"
HTTPTimeout = 10 * time.Second
CacheDuration = time.Minute

// NoVersion is returned by the version endpoint when there is no valid target version.
// This can be caused by the target version being incompatible with the cluster version.
NoVersion = "none"
)
4 changes: 3 additions & 1 deletion integrations/kube-agent-updater/pkg/controller/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"

"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/version"
)

// DeploymentVersionUpdater Reconciles a podSpec by changing its image
Expand Down Expand Up @@ -74,7 +76,7 @@ func (r *DeploymentVersionUpdater) Reconcile(ctx context.Context, req ctrl.Reque

image, err := r.GetVersion(ctx, &obj, currentVersion)
var (
noNewVersionErr *NoNewVersionError
noNewVersionErr *version.NoNewVersionError
maintenanceErr *MaintenanceNotTriggeredError
)
switch {
Expand Down
19 changes: 0 additions & 19 deletions integrations/kube-agent-updater/pkg/controller/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ limitations under the License.

package controller

import (
"fmt"
)

// MaintenanceNotTriggeredError indicates that no trigger returned true and the controller did not reconcile.
type MaintenanceNotTriggeredError struct {
Message string `json:"message"`
Expand All @@ -32,18 +28,3 @@ func (e *MaintenanceNotTriggeredError) Error() string {
}
return "maintenance not triggered"
}

// NoNewVersionError indicates that no new version was found and the controller did not reconcile.
type NoNewVersionError struct {
Message string `json:"message"`
CurrentVersion string `json:"currentVersion"`
NextVersion string `json:"nextVersion"`
}

// Error returns log friendly description of an error
func (e *NoNewVersionError) Error() string {
if e.Message != "" {
return e.Message
}
return fmt.Sprintf("no new version (current: %q, next: %q)", e.CurrentVersion, e.NextVersion)
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"

"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/podutils"
"github.com/gravitational/teleport/integrations/kube-agent-updater/pkg/version"
)

type StatefulSetVersionUpdater struct {
Expand Down Expand Up @@ -99,7 +100,7 @@ func (r *StatefulSetVersionUpdater) Reconcile(ctx context.Context, req ctrl.Requ

image, err := r.GetVersion(ctx, &obj, currentVersion)
var (
noNewVersionErr *NoNewVersionError
noNewVersionErr *version.NoNewVersionError
maintenanceErr *MaintenanceNotTriggeredError
)
switch {
Expand Down
2 changes: 1 addition & 1 deletion integrations/kube-agent-updater/pkg/controller/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (r *VersionUpdater) GetVersion(ctx context.Context, obj client.Object, curr

log.Info("New version candidate", "nextVersion", nextVersion)
if !version.ValidVersionChange(ctx, currentVersion, nextVersion) {
return nil, &NoNewVersionError{CurrentVersion: currentVersion, NextVersion: nextVersion}
return nil, &version.NoNewVersionError{CurrentVersion: currentVersion, NextVersion: nextVersion}
}

log.Info("Version change is valid, building img candidate")
Expand Down
Loading