mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-22 07:28:06 +00:00
Migrate main.go
This commit is contained in:
@@ -49,9 +49,9 @@ var log = logf.Log.WithName("controller_onepassworditem")
|
|||||||
|
|
||||||
// OnePasswordItemReconciler reconciles a OnePasswordItem object
|
// OnePasswordItemReconciler reconciles a OnePasswordItem object
|
||||||
type OnePasswordItemReconciler struct {
|
type OnePasswordItemReconciler struct {
|
||||||
kubeClient kubeClient.Client
|
Client kubeClient.Client
|
||||||
scheme *runtime.Scheme
|
Scheme *runtime.Scheme
|
||||||
opConnectClient connect.Client
|
OpConnectClient connect.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
//+kubebuilder:rbac:groups=onepassword.onepassword.com,resources=onepassworditems,verbs=get;list;watch;create;update;patch;delete
|
//+kubebuilder:rbac:groups=onepassword.onepassword.com,resources=onepassworditems,verbs=get;list;watch;create;update;patch;delete
|
||||||
@@ -72,7 +72,7 @@ func (r *OnePasswordItemReconciler) Reconcile(ctx context.Context, request ctrl.
|
|||||||
reqLogger.Info("Reconciling OnePasswordItem")
|
reqLogger.Info("Reconciling OnePasswordItem")
|
||||||
|
|
||||||
onepassworditem := &onepasswordv1.OnePasswordItem{}
|
onepassworditem := &onepasswordv1.OnePasswordItem{}
|
||||||
err := r.kubeClient.Get(context.Background(), request.NamespacedName, onepassworditem)
|
err := r.Client.Get(context.Background(), request.NamespacedName, onepassworditem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsNotFound(err) {
|
if errors.IsNotFound(err) {
|
||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
@@ -86,7 +86,7 @@ func (r *OnePasswordItemReconciler) Reconcile(ctx context.Context, request ctrl.
|
|||||||
// This is so we can handle cleanup of associated secrets properly
|
// This is so we can handle cleanup of associated secrets properly
|
||||||
if !utils.ContainsString(onepassworditem.ObjectMeta.Finalizers, finalizer) {
|
if !utils.ContainsString(onepassworditem.ObjectMeta.Finalizers, finalizer) {
|
||||||
onepassworditem.ObjectMeta.Finalizers = append(onepassworditem.ObjectMeta.Finalizers, finalizer)
|
onepassworditem.ObjectMeta.Finalizers = append(onepassworditem.ObjectMeta.Finalizers, finalizer)
|
||||||
if err := r.kubeClient.Update(context.Background(), onepassworditem); err != nil {
|
if err := r.Client.Update(context.Background(), onepassworditem); err != nil {
|
||||||
return reconcile.Result{}, err
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ func (r *OnePasswordItemReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||||||
|
|
||||||
func (r *OnePasswordItemReconciler) removeFinalizer(onePasswordItem *onepasswordv1.OnePasswordItem) error {
|
func (r *OnePasswordItemReconciler) removeFinalizer(onePasswordItem *onepasswordv1.OnePasswordItem) error {
|
||||||
onePasswordItem.ObjectMeta.Finalizers = utils.RemoveString(onePasswordItem.ObjectMeta.Finalizers, finalizer)
|
onePasswordItem.ObjectMeta.Finalizers = utils.RemoveString(onePasswordItem.ObjectMeta.Finalizers, finalizer)
|
||||||
if err := r.kubeClient.Update(context.Background(), onePasswordItem); err != nil {
|
if err := r.Client.Update(context.Background(), onePasswordItem); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -146,8 +146,8 @@ func (r *OnePasswordItemReconciler) cleanupKubernetesSecret(onePasswordItem *one
|
|||||||
kubernetesSecret.ObjectMeta.Name = onePasswordItem.Name
|
kubernetesSecret.ObjectMeta.Name = onePasswordItem.Name
|
||||||
kubernetesSecret.ObjectMeta.Namespace = onePasswordItem.Namespace
|
kubernetesSecret.ObjectMeta.Namespace = onePasswordItem.Namespace
|
||||||
|
|
||||||
r.kubeClient.Delete(context.Background(), kubernetesSecret)
|
r.Client.Delete(context.Background(), kubernetesSecret)
|
||||||
if err := r.kubeClient.Delete(context.Background(), kubernetesSecret); err != nil {
|
if err := r.Client.Delete(context.Background(), kubernetesSecret); err != nil {
|
||||||
if !errors.IsNotFound(err) {
|
if !errors.IsNotFound(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -162,13 +162,13 @@ func (r *OnePasswordItemReconciler) HandleOnePasswordItem(resource *onepasswordv
|
|||||||
secretType := resource.Type
|
secretType := resource.Type
|
||||||
autoRestart := annotations[op.RestartDeploymentsAnnotation]
|
autoRestart := annotations[op.RestartDeploymentsAnnotation]
|
||||||
|
|
||||||
item, err := onepassword.GetOnePasswordItemByPath(r.opConnectClient, resource.Spec.ItemPath)
|
item, err := onepassword.GetOnePasswordItemByPath(r.OpConnectClient, resource.Spec.ItemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to retrieve item: %v", err)
|
return fmt.Errorf("Failed to retrieve item: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create owner reference.
|
// Create owner reference.
|
||||||
gvk, err := apiutil.GVKForObject(resource, r.scheme)
|
gvk, err := apiutil.GVKForObject(resource, r.Scheme)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not to retrieve group version kind: %v", err)
|
return fmt.Errorf("could not to retrieve group version kind: %v", err)
|
||||||
}
|
}
|
||||||
@@ -179,5 +179,5 @@ func (r *OnePasswordItemReconciler) HandleOnePasswordItem(resource *onepasswordv
|
|||||||
UID: resource.GetUID(),
|
UID: resource.GetUID(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, annotations, ownerRef)
|
return kubeSecrets.CreateKubernetesSecretFromItem(r.Client, secretName, resource.Namespace, item, autoRestart, labels, secretType, annotations, ownerRef)
|
||||||
}
|
}
|
||||||
|
164
main.go
164
main.go
@@ -17,14 +17,29 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Password/connect-sdk-go/connect"
|
||||||
|
op "github.com/1Password/onepassword-operator/pkg/onepassword"
|
||||||
|
"github.com/1Password/onepassword-operator/pkg/utils"
|
||||||
|
"github.com/1Password/onepassword-operator/version"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
|
||||||
|
|
||||||
|
// sdkVersion "github.com/operator-framework/operator-sdk/version"
|
||||||
|
|
||||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||||
// to ensure that exec-entrypoint and run can make use of them.
|
// to ensure that exec-entrypoint and run can make use of them.
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
@@ -37,8 +52,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
scheme = runtime.NewScheme()
|
scheme = k8sruntime.NewScheme()
|
||||||
setupLog = ctrl.Log.WithName("setup")
|
setupLog = ctrl.Log.WithName("setup")
|
||||||
|
WatchNamespaceEnvVar = "WATCH_NAMESPACE"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -48,6 +64,14 @@ func init() {
|
|||||||
//+kubebuilder:scaffold:scheme
|
//+kubebuilder:scaffold:scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printVersion() {
|
||||||
|
setupLog.Info(fmt.Sprintf("Operator Version: %s", version.Version))
|
||||||
|
setupLog.Info(fmt.Sprintf("Go Version: %s", runtime.Version()))
|
||||||
|
setupLog.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH))
|
||||||
|
// TODO figure out how to get operator-sdk version
|
||||||
|
// setupLog.Info(fmt.Sprintf("Version of operator-sdk: %v", sdkVersion.Version))
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var metricsAddr string
|
var metricsAddr string
|
||||||
var enableLeaderElection bool
|
var enableLeaderElection bool
|
||||||
@@ -65,22 +89,45 @@ func main() {
|
|||||||
|
|
||||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||||
|
|
||||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
printVersion()
|
||||||
|
|
||||||
|
namespace := os.Getenv(WatchNamespaceEnvVar)
|
||||||
|
|
||||||
|
options := ctrl.Options{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
|
Namespace: namespace,
|
||||||
MetricsBindAddress: metricsAddr,
|
MetricsBindAddress: metricsAddr,
|
||||||
Port: 9443,
|
Port: 9443,
|
||||||
HealthProbeBindAddress: probeAddr,
|
HealthProbeBindAddress: probeAddr,
|
||||||
LeaderElection: enableLeaderElection,
|
LeaderElection: enableLeaderElection,
|
||||||
LeaderElectionID: "c26807fd.onepassword.com",
|
LeaderElectionID: "c26807fd.onepassword.com",
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Add support for MultiNamespace set in WATCH_NAMESPACE (e.g ns1,ns2)
|
||||||
|
// Note that this is not intended to be used for excluding namespaces, this is better done via a Predicate
|
||||||
|
// Also note that you may face performance issues when using this with a high number of namespaces.
|
||||||
|
if strings.Contains(namespace, ",") {
|
||||||
|
options.Namespace = ""
|
||||||
|
options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
setupLog.Error(err, "unable to start manager")
|
setupLog.Error(err, "unable to start manager")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup One Password Client
|
||||||
|
opConnectClient, err := connect.NewClientFromEnvironment()
|
||||||
|
if err != nil {
|
||||||
|
setupLog.Error(err, "failed to create 1Password client")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
if err = (&controllers.OnePasswordItemReconciler{
|
if err = (&controllers.OnePasswordItemReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
|
OpConnectClient: opConnectClient,
|
||||||
}).SetupWithManager(mgr); err != nil {
|
}).SetupWithManager(mgr); err != nil {
|
||||||
setupLog.Error(err, "unable to create controller", "controller", "OnePasswordItem")
|
setupLog.Error(err, "unable to create controller", "controller", "OnePasswordItem")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -101,4 +148,107 @@ func main() {
|
|||||||
setupLog.Error(err, "problem running manager")
|
setupLog.Error(err, "problem running manager")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deploymentNamespace, err := utils.GetOperatorNamespace()
|
||||||
|
if err != nil {
|
||||||
|
setupLog.Error(err, "Failed to get namespace")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Setup 1PasswordConnect
|
||||||
|
if shouldManageConnect() {
|
||||||
|
setupLog.Info("Automated Connect Management Enabled")
|
||||||
|
go func() {
|
||||||
|
connectStarted := false
|
||||||
|
for !connectStarted {
|
||||||
|
err := op.SetupConnect(mgr.GetClient(), deploymentNamespace)
|
||||||
|
// Cache Not Started is an acceptable error. Retry until cache is started.
|
||||||
|
if err != nil && !errors.Is(err, &cache.ErrCacheNotStarted{}) {
|
||||||
|
setupLog.Error(err, "")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
connectStarted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
setupLog.Info("Automated Connect Management Disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Configure Metrics Service. See: https://sdk.operatorframework.io/docs/building-operators/golang/migration/#export-metrics
|
||||||
|
|
||||||
|
// Setup update secrets task
|
||||||
|
updatedSecretsPoller := op.NewManager(mgr.GetClient(), opConnectClient, shouldAutoRestartDeployments())
|
||||||
|
done := make(chan bool)
|
||||||
|
ticker := time.NewTicker(getPollingIntervalForUpdatingSecrets())
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
ticker.Stop()
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
err := updatedSecretsPoller.UpdateKubernetesSecretsTask()
|
||||||
|
if err != nil {
|
||||||
|
setupLog.Error(err, "error running update kubernetes secret task")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Start the Cmd
|
||||||
|
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
|
||||||
|
setupLog.Error(err, "Manager exited non-zero")
|
||||||
|
done <- true
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const manageConnect = "MANAGE_CONNECT"
|
||||||
|
|
||||||
|
func shouldManageConnect() bool {
|
||||||
|
shouldManageConnect, found := os.LookupEnv(manageConnect)
|
||||||
|
if found {
|
||||||
|
shouldManageConnectBool, err := strconv.ParseBool(strings.ToLower(shouldManageConnect))
|
||||||
|
if err != nil {
|
||||||
|
setupLog.Error(err, "")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return shouldManageConnectBool
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const envPollingIntervalVariable = "POLLING_INTERVAL"
|
||||||
|
const defaultPollingInterval = 600
|
||||||
|
|
||||||
|
func getPollingIntervalForUpdatingSecrets() time.Duration {
|
||||||
|
timeInSecondsString, found := os.LookupEnv(envPollingIntervalVariable)
|
||||||
|
if found {
|
||||||
|
timeInSeconds, err := strconv.Atoi(timeInSecondsString)
|
||||||
|
if err == nil {
|
||||||
|
return time.Duration(timeInSeconds) * time.Second
|
||||||
|
}
|
||||||
|
setupLog.Info("Invalid value set for polling interval. Must be a valid integer.")
|
||||||
|
}
|
||||||
|
|
||||||
|
setupLog.Info(fmt.Sprintf("Using default polling interval of %v seconds", defaultPollingInterval))
|
||||||
|
return time.Duration(defaultPollingInterval) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
const restartDeploymentsEnvVariable = "AUTO_RESTART"
|
||||||
|
|
||||||
|
func shouldAutoRestartDeployments() bool {
|
||||||
|
shouldAutoRestartDeployments, found := os.LookupEnv(restartDeploymentsEnvVariable)
|
||||||
|
if found {
|
||||||
|
shouldAutoRestartDeploymentsBool, err := strconv.ParseBool(strings.ToLower(shouldAutoRestartDeployments))
|
||||||
|
if err != nil {
|
||||||
|
setupLog.Error(err, "")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return shouldAutoRestartDeploymentsBool
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
45
pkg/utils/k8sutil.go
Normal file
45
pkg/utils/k8sutil.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ForceRunModeEnv = "OSDK_FORCE_RUN_MODE"
|
||||||
|
|
||||||
|
type RunModeType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LocalRunMode RunModeType = "local"
|
||||||
|
ClusterRunMode RunModeType = "cluster"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNoNamespace indicates that a namespace could not be found for the current
|
||||||
|
// environment
|
||||||
|
var ErrNoNamespace = fmt.Errorf("namespace not found for current environment")
|
||||||
|
|
||||||
|
// ErrRunLocal indicates that the operator is set to run in local mode (this error
|
||||||
|
// is returned by functions that only work on operators running in cluster mode)
|
||||||
|
var ErrRunLocal = fmt.Errorf("operator run mode forced to local")
|
||||||
|
|
||||||
|
// GetOperatorNamespace returns the namespace the operator should be running in.
|
||||||
|
func GetOperatorNamespace() (string, error) {
|
||||||
|
if isRunModeLocal() {
|
||||||
|
return "", ErrRunLocal
|
||||||
|
}
|
||||||
|
nsBytes, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", ErrNoNamespace
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ns := strings.TrimSpace(string(nsBytes))
|
||||||
|
return ns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isRunModeLocal() bool {
|
||||||
|
return os.Getenv(ForceRunModeEnv) == string(LocalRunMode)
|
||||||
|
}
|
6
version/version.go
Normal file
6
version/version.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package version
|
||||||
|
|
||||||
|
// TODO figure out if this package makes sense.
|
||||||
|
var (
|
||||||
|
Version = "0.0.1"
|
||||||
|
)
|
Reference in New Issue
Block a user