mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-21 15:08:06 +00:00

* Add missing improvements from Operator SDK 1.34.1 These were not mentioned in the upgrade documentation for version 1.34.x (https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.34.0/), but I've found them by compating the release with the previous one (https://github.com/operator-framework/operator-sdk/compare/v1.33.0...v1.34.1). * Upgrade to Operator SDK 1.36.0 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.36.0/ Key differences: - Go packages `k8s.io/*` are already at a version higher than the one in the upgrade. - `ENVTEST_K8S_VERSION` is at a version higher than the one in the upgrade - We didn't have the golangci-lint make command before, thus we only needed to add things. * Upgrade to Operator SDK 1.38.0 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.38.0/ * Upgrade to Operator SDK 1.39.0 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.39.0/ * Upgrade to Operator SDK 1.40.0 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.40.0/ I didn't do the "Add app.kubernetes.io/name label to your manifests" since it seems that we have it already, and it's customized. * Address lint errors * Update golangci-lint version used to support Go 1.24 * Improve workflows - Make workflow targets more specific. - Make build workflow only build (i.e. remove test part of it). - Rearrange steps and improve naming for build workflow. * Add back deleted test Initially the test has been removed due to lint saying that it was duplicate code, but it falsely errored since the values are different. * Improve code and add missing upgrade pieces * Upgrade to Operator SDK 1.41.1 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.41.0/ Upgrading to 1.41.1 from 1.40.0 doesn't have any migration steps. Key elements: - Upgrade to golangci-lint v2 - Made the manifests using the updated controller tools * Address linter errors golanci-lint v2 seems to be more robust than the previous one, which is beneficial. Thus, we address the linter errors thrown by v2 and improve our code even further. * Add Makefile improvements These were brought in by comparing the Makefile of a freshly created operator using the latest operator-sdk with ours. * Add missing default kustomization for 1.40.0 upgrade * Bring default kustomization to latest version This is done by putting the file's content from a newly-generated operator. * Switch metrics-bind-address default value back to 8080 This ensures that the upgrade is backwards-compatible. * Add webhook-related scaffolding This enables us to easily add support for webhooks by running `operator-sdk create webhook` whenever we want to add them. * Fix typo
242 lines
6.9 KiB
Go
242 lines
6.9 KiB
Go
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2020-2024 1Password
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
package controller
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"testing"
|
|
"time"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
"github.com/stretchr/testify/mock"
|
|
|
|
"k8s.io/client-go/kubernetes/scheme"
|
|
"k8s.io/client-go/rest"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
|
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
|
|
|
onepasswordcomv1 "github.com/1Password/onepassword-operator/api/v1"
|
|
"github.com/1Password/onepassword-operator/pkg/mocks"
|
|
"github.com/1Password/onepassword-operator/pkg/onepassword/model"
|
|
// +kubebuilder:scaffold:imports
|
|
)
|
|
|
|
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
|
|
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
|
|
|
|
const (
|
|
username = "test-user"
|
|
password = "QmHumKc$mUeEem7caHtbaBaJ"
|
|
|
|
username2 = "test-user2"
|
|
password2 = "4zotzqDqXKasLFT2jzTs"
|
|
|
|
annotationRegExpString = "^operator.1password.io\\/[a-zA-Z\\.]+"
|
|
)
|
|
|
|
// Define utility constants for object names and testing timeouts/durations and intervals.
|
|
const (
|
|
namespace = "default"
|
|
|
|
timeout = time.Second * 10
|
|
duration = time.Second * 10
|
|
interval = time.Millisecond * 250
|
|
)
|
|
|
|
var (
|
|
cfg *rest.Config
|
|
k8sClient client.Client
|
|
testEnv *envtest.Environment
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
onePasswordItemReconciler *OnePasswordItemReconciler
|
|
deploymentReconciler *DeploymentReconciler
|
|
mockGetItemByIDFunc *mock.Call
|
|
|
|
item1 = &TestItem{
|
|
ItemID: "nwrhuano7bcwddcviubpp4mhfq",
|
|
VaultID: "hfnjvi6aymbsnfc2xeeoheizda",
|
|
Name: "test-item",
|
|
Version: 123,
|
|
Path: "vaults/hfnjvi6aymbsnfc2xeeoheizda/items/nwrhuano7bcwddcviubpp4mhfq",
|
|
Data: map[string]string{
|
|
"username": username,
|
|
"password": password,
|
|
},
|
|
SecretData: map[string][]byte{
|
|
"password": []byte(password),
|
|
"username": []byte(username),
|
|
},
|
|
}
|
|
|
|
item2 = &TestItem{
|
|
ItemID: "nwrhuano7bcwddcviubpp4mhf2",
|
|
VaultID: "hfnjvi6aymbsnfc2xeeoheizd2",
|
|
Name: "test-item2",
|
|
Path: "vaults/hfnjvi6aymbsnfc2xeeoheizd2/items/nwrhuano7bcwddcviubpp4mhf2",
|
|
Version: 456,
|
|
Data: map[string]string{
|
|
"username": username2,
|
|
"password": password2,
|
|
},
|
|
SecretData: map[string][]byte{
|
|
"password": []byte(password2),
|
|
"username": []byte(username2),
|
|
},
|
|
}
|
|
)
|
|
|
|
type TestItem struct {
|
|
ItemID string
|
|
VaultID string
|
|
Name string
|
|
Version int
|
|
Path string
|
|
Data map[string]string
|
|
SecretData map[string][]byte
|
|
}
|
|
|
|
func (ti *TestItem) ToModel() *model.Item {
|
|
item := &model.Item{}
|
|
item.Version = ti.Version
|
|
item.VaultID = ti.VaultID
|
|
item.ID = ti.ItemID
|
|
|
|
item.Fields = []model.ItemField{}
|
|
for k, v := range ti.Data {
|
|
item.Fields = append(item.Fields, model.ItemField{Label: k, Value: v})
|
|
}
|
|
|
|
return item
|
|
}
|
|
|
|
func TestAPIs(t *testing.T) {
|
|
RegisterFailHandler(Fail)
|
|
|
|
RunSpecs(t, "Controller Suite")
|
|
}
|
|
|
|
var _ = BeforeSuite(func() {
|
|
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
|
|
|
|
ctx, cancel = context.WithCancel(context.TODO())
|
|
|
|
By("bootstrapping test environment")
|
|
testEnv = &envtest.Environment{
|
|
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
|
|
ErrorIfCRDPathMissing: true,
|
|
}
|
|
|
|
// Retrieve the first found binary directory to allow running tests from IDEs
|
|
if getFirstFoundEnvTestBinaryDir() != "" {
|
|
testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir()
|
|
}
|
|
|
|
var err error
|
|
// cfg is defined in this file globally.
|
|
cfg, err = testEnv.Start()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(cfg).NotTo(BeNil())
|
|
|
|
err = onepasswordcomv1.AddToScheme(scheme.Scheme)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// +kubebuilder:scaffold:scheme
|
|
|
|
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(k8sClient).NotTo(BeNil())
|
|
|
|
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
|
|
Scheme: scheme.Scheme,
|
|
})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
mockOpClient := &mocks.TestClient{}
|
|
mockGetItemByIDFunc = mockOpClient.On("GetItemByID", mock.Anything, mock.Anything)
|
|
|
|
onePasswordItemReconciler = &OnePasswordItemReconciler{
|
|
Client: k8sManager.GetClient(),
|
|
Scheme: k8sManager.GetScheme(),
|
|
OpClient: mockOpClient,
|
|
}
|
|
err = (onePasswordItemReconciler).SetupWithManager(k8sManager)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
r, _ := regexp.Compile(annotationRegExpString)
|
|
deploymentReconciler = &DeploymentReconciler{
|
|
Client: k8sManager.GetClient(),
|
|
Scheme: k8sManager.GetScheme(),
|
|
OpClient: mockOpClient,
|
|
OpAnnotationRegExp: r,
|
|
}
|
|
err = (deploymentReconciler).SetupWithManager(k8sManager)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
err = k8sManager.Start(ctx)
|
|
Expect(err).ToNot(HaveOccurred(), "failed to run manager")
|
|
}()
|
|
|
|
})
|
|
|
|
var _ = AfterSuite(func() {
|
|
cancel()
|
|
By("tearing down the test environment")
|
|
err := testEnv.Stop()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path.
|
|
// ENVTEST-based tests depend on specific binaries, usually located in paths set by
|
|
// controller-runtime. When running tests directly (e.g., via an IDE) without using
|
|
// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured.
|
|
//
|
|
// This function streamlines the process by finding the required binaries, similar to
|
|
// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are
|
|
// properly set up, run 'make setup-envtest' beforehand.
|
|
func getFirstFoundEnvTestBinaryDir() string {
|
|
basePath := filepath.Join("..", "..", "bin", "k8s")
|
|
entries, err := os.ReadDir(basePath)
|
|
if err != nil {
|
|
logf.Log.Error(err, "Failed to read directory", "path", basePath)
|
|
return ""
|
|
}
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
return filepath.Join(basePath, entry.Name())
|
|
}
|
|
}
|
|
return ""
|
|
}
|