No more controllers

This commit is contained in:
Marc Campbell
2019-12-24 19:38:47 +00:00
parent 3955dab9bc
commit 2b97e524e3
30 changed files with 9 additions and 2283 deletions

View File

@@ -39,6 +39,7 @@ func Test_textAnalyze(t *testing.T) {
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "text-collector-1",
Message: "pass",
},
files: map[string][]byte{
@@ -68,6 +69,7 @@ func Test_textAnalyze(t *testing.T) {
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-2",
Message: "fail",
},
files: map[string][]byte{
@@ -94,10 +96,11 @@ func Test_textAnalyze(t *testing.T) {
RegexPattern: "",
},
expectResult: AnalyzeResult{
IsPass: true,
IsPass: false,
IsWarn: false,
IsFail: false,
Message: "success",
IsFail: true,
Title: "",
Message: "Invalid analyzer",
},
files: map[string][]byte{
"text-collector-3/cfile-3.txt": []byte("Connection to service succeeded"),
@@ -126,6 +129,7 @@ func Test_textAnalyze(t *testing.T) {
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "text-collector-5",
Message: "success",
},
files: map[string][]byte{
@@ -155,6 +159,7 @@ func Test_textAnalyze(t *testing.T) {
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-4",
Message: "fail",
},
files: map[string][]byte{
@@ -184,6 +189,7 @@ func Test_textAnalyze(t *testing.T) {
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "text-collector-6",
Message: "fail",
},
files: map[string][]byte{

View File

@@ -1,26 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"github.com/replicatedhq/troubleshoot/pkg/controller/analyzer"
)
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, analyzer.Add)
}

View File

@@ -1,26 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"github.com/replicatedhq/troubleshoot/pkg/controller/analyzerjob"
)
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, analyzerjob.Add)
}

View File

@@ -1,26 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"github.com/replicatedhq/troubleshoot/pkg/controller/collector"
)
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, collector.Add)
}

View File

@@ -1,26 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"github.com/replicatedhq/troubleshoot/pkg/controller/collectorjob"
)
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, collectorjob.Add)
}

View File

@@ -1,26 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"github.com/replicatedhq/troubleshoot/pkg/controller/preflight"
)
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, preflight.Add)
}

View File

@@ -1,26 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"github.com/replicatedhq/troubleshoot/pkg/controller/preflightjob"
)
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, preflightjob.Add)
}

View File

@@ -1,167 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"reflect"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("controller")
/**
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
* business logic. Delete these comments after modifying this file.*
*/
// Add creates a new Analyzer Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileAnalyzer{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("analyzer-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to Analyzer
err = c.Watch(&source.Kind{Type: &troubleshootv1beta1.Analyzer{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
// TODO(user): Modify this to be the types you create
// Uncomment watch a Deployment created by Analyzer - change this for objects you create
err = c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &troubleshootv1beta1.Analyzer{},
})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcileAnalyzer{}
// ReconcileAnalyzer reconciles a Analyzer object
type ReconcileAnalyzer struct {
client.Client
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a Analyzer object and makes changes based on the state read
// and what is in the Analyzer.Spec
// TODO(user): Modify this Reconcile function to implement your Controller logic. The scaffolding writes
// a Deployment as an example
// Automatically generate RBAC rules to allow the Controller to read and write Deployments
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=analyzers,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=analyzers/status,verbs=get;update;patch
func (r *ReconcileAnalyzer) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Analyzer instance
instance := &troubleshootv1beta1.Analyzer{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
// TODO(user): Change this to be the object type created by your controller
// Define the desired Deployment object
deploy := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name + "-deployment",
Namespace: instance.Namespace,
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"deployment": instance.Name + "-deployment"},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"deployment": instance.Name + "-deployment"}},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "nginx",
Image: "nginx",
},
},
},
},
},
}
if err := controllerutil.SetControllerReference(instance, deploy, r.scheme); err != nil {
return reconcile.Result{}, err
}
// TODO(user): Change this for the object type created by your controller
// Check if the Deployment already exists
found := &appsv1.Deployment{}
err = r.Get(context.TODO(), types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, found)
if err != nil && errors.IsNotFound(err) {
log.Info("Creating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
err = r.Create(context.TODO(), deploy)
return reconcile.Result{}, err
} else if err != nil {
return reconcile.Result{}, err
}
// TODO(user): Change this for the object type created by your controller
// Update the found object and write the result back if there are any changes
if !reflect.DeepEqual(deploy.Spec, found.Spec) {
found.Spec = deploy.Spec
log.Info("Updating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
err = r.Update(context.TODO(), found)
if err != nil {
return reconcile.Result{}, err
}
}
return reconcile.Result{}, nil
}

View File

@@ -1,75 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"github.com/replicatedhq/troubleshoot/pkg/apis"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -1,25 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"testing"
)
func TestReconcile(t *testing.T) {
}

View File

@@ -1,167 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzerjob
import (
"context"
"reflect"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("controller")
/**
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
* business logic. Delete these comments after modifying this file.*
*/
// Add creates a new AnalyzerJob Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileAnalyzerJob{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("analyzerjob-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to AnalyzerJob
err = c.Watch(&source.Kind{Type: &troubleshootv1beta1.AnalyzerJob{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
// TODO(user): Modify this to be the types you create
// Uncomment watch a Deployment created by AnalyzerJob - change this for objects you create
err = c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &troubleshootv1beta1.AnalyzerJob{},
})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcileAnalyzerJob{}
// ReconcileAnalyzerJob reconciles a AnalyzerJob object
type ReconcileAnalyzerJob struct {
client.Client
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a AnalyzerJob object and makes changes based on the state read
// and what is in the AnalyzerJob.Spec
// TODO(user): Modify this Reconcile function to implement your Controller logic. The scaffolding writes
// a Deployment as an example
// Automatically generate RBAC rules to allow the Controller to read and write Deployments
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=analyzerjobs,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=analyzerjobs/status,verbs=get;update;patch
func (r *ReconcileAnalyzerJob) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the AnalyzerJob instance
instance := &troubleshootv1beta1.AnalyzerJob{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
// TODO(user): Change this to be the object type created by your controller
// Define the desired Deployment object
deploy := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name + "-deployment",
Namespace: instance.Namespace,
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"deployment": instance.Name + "-deployment"},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"deployment": instance.Name + "-deployment"}},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "nginx",
Image: "nginx",
},
},
},
},
},
}
if err := controllerutil.SetControllerReference(instance, deploy, r.scheme); err != nil {
return reconcile.Result{}, err
}
// TODO(user): Change this for the object type created by your controller
// Check if the Deployment already exists
found := &appsv1.Deployment{}
err = r.Get(context.TODO(), types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, found)
if err != nil && errors.IsNotFound(err) {
log.Info("Creating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
err = r.Create(context.TODO(), deploy)
return reconcile.Result{}, err
} else if err != nil {
return reconcile.Result{}, err
}
// TODO(user): Change this for the object type created by your controller
// Update the found object and write the result back if there are any changes
if !reflect.DeepEqual(deploy.Spec, found.Spec) {
found.Spec = deploy.Spec
log.Info("Updating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
err = r.Update(context.TODO(), found)
if err != nil {
return reconcile.Result{}, err
}
}
return reconcile.Result{}, nil
}

View File

@@ -1,75 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzerjob
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"github.com/replicatedhq/troubleshoot/pkg/apis"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -1,25 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzerjob
import (
"testing"
)
func TestReconcile(t *testing.T) {
}

View File

@@ -1,105 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package collector
import (
"context"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("controller")
// Add creates a new Collector Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileCollector{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("collector-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to Collector
err = c.Watch(&source.Kind{Type: &troubleshootv1beta1.Collector{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcileCollector{}
// ReconcileCollector reconciles a Collector object
type ReconcileCollector struct {
client.Client
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a Collector object and makes changes based on the state read
// and what is in the Collector.Spec
// Automatically generate RBAC rules to allow the Controller to read and write Deployments
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=collectors,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=collectors/status,verbs=get;update;patch
func (r *ReconcileCollector) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Collector instance
instance := &troubleshootv1beta1.Collector{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
// The troubleshoot spec doesn't really do anything. the work
// is performed in the troubleshootjob crd. this one is really
// primarily used as a packaging format2
return reconcile.Result{}, nil
}
func (r *ReconcileCollector) getCollectorConfigMapSpec(instance *troubleshootv1beta1.Collector) (*v1.ConfigMap, error) {
configMap := v1.ConfigMap{}
return &configMap, nil
}

View File

@@ -1,75 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package collector
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"github.com/replicatedhq/troubleshoot/pkg/apis"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -1,25 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package collector
import (
"testing"
)
func TestReconcile(t *testing.T) {
}

View File

@@ -1,127 +0,0 @@
package collectorjob
import (
"context"
"fmt"
"time"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
corev1 "k8s.io/api/core/v1"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
func (r *ReconcileCollectorJob) createCollectorServer(instance *troubleshootv1beta1.CollectorJob) error {
name := fmt.Sprintf("%s-%s", instance.Name, "collector")
namespacedName := types.NamespacedName{
Name: name,
Namespace: instance.Namespace,
}
found := &corev1.Pod{}
err := r.Get(context.Background(), namespacedName, found)
if err == nil || !kuberneteserrors.IsNotFound(err) {
return err
}
imageName := "replicated/troubleshoot:latest"
imagePullPolicy := corev1.PullAlways
if instance.Spec.Image != "" {
imageName = instance.Spec.Image
}
if instance.Spec.ImagePullPolicy != "" {
imagePullPolicy = corev1.PullPolicy(instance.Spec.ImagePullPolicy)
}
podLabels := make(map[string]string)
podLabels["collector"] = instance.Name
podLabels["troubleshoot-role"] = "collector"
pod := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: instance.Namespace,
Labels: podLabels,
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Pod",
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyNever,
Containers: []corev1.Container{
{
Image: imageName,
ImagePullPolicy: imagePullPolicy,
Name: "collector",
Command: []string{"collector"},
Args: []string{"server"},
Ports: []corev1.ContainerPort{
{
Name: "http",
ContainerPort: 8000,
},
},
},
},
},
}
if err := controllerutil.SetControllerReference(instance, &pod, r.scheme); err != nil {
return err
}
if err := r.Create(context.Background(), &pod); err != nil {
return err
}
service := corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: instance.Namespace,
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Service",
},
Spec: corev1.ServiceSpec{
Selector: podLabels,
Type: corev1.ServiceTypeClusterIP,
Ports: []corev1.ServicePort{
{
Name: "http",
Port: 8000,
TargetPort: intstr.FromInt(8000),
},
},
},
}
if err := controllerutil.SetControllerReference(instance, &service, r.scheme); err != nil {
return err
}
if err := r.Create(context.Background(), &service); err != nil {
return err
}
instance.Status.ServerPodName = name
instance.Status.ServerPodNamespace = instance.Namespace
instance.Status.ServerPodPort = 8000
instance.Status.IsServerReady = true
// wait for the server to be ready
// TODO
time.Sleep(time.Second * 5)
if err := r.Update(context.Background(), instance); err != nil {
return err
}
return nil
}

View File

@@ -1,439 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package collectorjob
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"math/rand"
"net/http"
"os"
"time"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
collectrunner "github.com/replicatedhq/troubleshoot/pkg/collect"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
"github.com/replicatedhq/troubleshoot/pkg/logger"
"gopkg.in/yaml.v2"
corev1 "k8s.io/api/core/v1"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("controller")
// Add creates a new CollectorJob Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileCollectorJob{
Client: mgr.GetClient(),
config: mgr.GetConfig(),
scheme: mgr.GetScheme(),
}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("collectorjob-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to CollectorJob
err = c.Watch(&source.Kind{Type: &troubleshootv1beta1.CollectorJob{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &troubleshootv1beta1.CollectorJob{},
})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcileCollectorJob{}
// ReconcileCollectorJob reconciles a CollectorJob object
type ReconcileCollectorJob struct {
client.Client
config *rest.Config
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a CollectorJob object and makes changes based on the state read
// and what is in the CollectorJob.Spec
// TODO(user): Modify this Reconcile function to implement your Controller logic. The scaffolding writes
// a Deployment as an example
// Automatically generate RBAC rules to allow the Controller to read and write Deployments
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=collectorjobs,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=collectorjobs/status,verbs=get;update;patch
func (r *ReconcileCollectorJob) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the CollectorJob instance
instance := &troubleshootv1beta1.CollectorJob{}
err := r.Get(context.Background(), request.NamespacedName, instance)
if err != nil {
if kuberneteserrors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
// for a new object, create the http server
if !instance.Status.IsServerReady {
if err := r.createCollectorServer(instance); err != nil {
return reconcile.Result{}, err
}
}
namespace := instance.Namespace
if instance.Spec.Collector.Namespace != "" {
namespace = instance.Spec.Collector.Namespace
}
collectorSpec, err := r.getCollectorSpec(namespace, instance.Spec.Collector.Name)
if err != nil {
return reconcile.Result{}, err
}
for _, collector := range collectorSpec.Spec.Collectors {
if err := r.reconileOneCollectorJob(instance, collector); err != nil {
return reconcile.Result{}, err
}
}
return reconcile.Result{}, nil
}
func (r *ReconcileCollectorJob) getCollectorSpec(namespace string, name string) (*troubleshootv1beta1.Collector, error) {
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(r.config)
if err != nil {
return nil, err
}
collector, err := troubleshootClient.Collectors(namespace).Get(name, metav1.GetOptions{})
if err != nil {
if kuberneteserrors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
return collector, nil
}
func (r *ReconcileCollectorJob) reconileOneCollectorJob(instance *troubleshootv1beta1.CollectorJob, collect *troubleshootv1beta1.Collect) error {
if contains(instance.Status.Running, collectrunner.DeterministicIDForCollector(collect)) {
collectorPod, err := r.getCollectorPod(instance, collect)
if err != nil {
return err
}
if collectorPod.Status.Phase == corev1.PodFailed {
instance.Status.Failed = append(instance.Status.Failed, collectrunner.DeterministicIDForCollector(collect))
instance.Status.Running = remove(instance.Status.Running, collectrunner.DeterministicIDForCollector(collect))
if err := r.Update(context.Background(), instance); err != nil {
return err
}
return nil
}
if collectorPod.Status.Phase == corev1.PodSucceeded {
// Get the logs
podLogOpts := corev1.PodLogOptions{}
k8sClient, err := kubernetes.NewForConfig(r.config)
if err != nil {
return err
}
req := k8sClient.CoreV1().Pods(collectorPod.Namespace).GetLogs(collectorPod.Name, &podLogOpts)
podLogs, err := req.Stream()
if err != nil {
return err
}
defer podLogs.Close()
buf := new(bytes.Buffer)
_, err = io.Copy(buf, podLogs)
if err != nil {
return err
}
client := &http.Client{}
serviceURI := ""
// For local dev, it's useful to run the manager out of the cluster
// but this is difficult to connect to the collector service running in-cluster
// so, we can create a local port-foward to get back into the cluster.
stopCh := make(chan struct{}, 1)
if os.Getenv("TROUBLESHOOT_EXTERNAL_MANAGER") != "" {
logger.Printf("setting up port forwarding because the manager is not running in the cluster\n")
// this isn't likely to be very solid
localPort := 3000 + rand.New(rand.NewSource(time.Now().UnixNano())).Intn(999)
ch, err := k8sutil.PortForward(r.config, localPort, 8000, instance.Namespace, instance.Name+"-collector")
if err != nil {
return err
}
stopCh = ch
serviceURI = fmt.Sprintf("http://localhost:%d", localPort)
} else {
serviceURI = fmt.Sprintf("http://%s-collector.%s.svc.cluster.local:8000", instance.Name, instance.Namespace)
}
request, err := http.NewRequest("PUT", serviceURI, buf)
if err != nil {
return err
}
request.ContentLength = int64(len(buf.String()))
request.Header.Add("collector-id", collectrunner.DeterministicIDForCollector(collect))
resp, err := client.Do(request)
if err != nil {
return err
}
if resp.StatusCode != 201 {
return errors.New("failed to send logs to collector")
}
if os.Getenv("TROUBLESHOOT_EXTERNAL_MANAGER") != "" {
logger.Printf("stopping port forwarding\n")
close(stopCh)
}
instance.Status.Successful = append(instance.Status.Successful, collectrunner.DeterministicIDForCollector(collect))
instance.Status.Running = remove(instance.Status.Running, collectrunner.DeterministicIDForCollector(collect))
if err := r.Update(context.Background(), instance); err != nil {
return err
}
return nil
}
return nil
}
if err := r.createSpecInConfigMap(instance, collect); err != nil {
return err
}
if err := r.createCollectorPod(instance, collect); err != nil {
return err
}
return nil
}
func (r *ReconcileCollectorJob) createSpecInConfigMap(instance *troubleshootv1beta1.CollectorJob, collector *troubleshootv1beta1.Collect) error {
name := fmt.Sprintf("%s-%s", instance.Name, collectrunner.DeterministicIDForCollector(collector))
namespacedName := types.NamespacedName{
Name: name,
Namespace: instance.Namespace,
}
found := &corev1.ConfigMap{}
err := r.Get(context.Background(), namespacedName, found)
if err == nil || !kuberneteserrors.IsNotFound(err) {
return err
}
specContents, err := yaml.Marshal(collector)
if err != nil {
return err
}
specData := make(map[string]string)
specData["collector.yaml"] = string(specContents)
configMap := corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: instance.Namespace,
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ConfigMap",
},
Data: specData,
}
if err := controllerutil.SetControllerReference(instance, &configMap, r.scheme); err != nil {
return err
}
if err := r.Create(context.Background(), &configMap); err != nil {
return err
}
return nil
}
func (r *ReconcileCollectorJob) getCollectorPod(instance *troubleshootv1beta1.CollectorJob, collector *troubleshootv1beta1.Collect) (*corev1.Pod, error) {
name := fmt.Sprintf("%s-%s", instance.Name, collectrunner.DeterministicIDForCollector(collector))
namespacedName := types.NamespacedName{
Name: name,
Namespace: instance.Namespace,
}
pod := &corev1.Pod{}
err := r.Get(context.Background(), namespacedName, pod)
if err != nil {
return nil, err
}
return pod, nil
}
func (r *ReconcileCollectorJob) createCollectorPod(instance *troubleshootv1beta1.CollectorJob, collector *troubleshootv1beta1.Collect) error {
name := fmt.Sprintf("%s-%s", instance.Name, collectrunner.DeterministicIDForCollector(collector))
namespacedName := types.NamespacedName{
Name: name,
Namespace: instance.Namespace,
}
found := &corev1.Pod{}
err := r.Get(context.Background(), namespacedName, found)
if err == nil || !kuberneteserrors.IsNotFound(err) {
return err
}
imageName := "replicated/troubleshoot:latest"
imagePullPolicy := corev1.PullAlways
if instance.Spec.Image != "" {
imageName = instance.Spec.Image
}
if instance.Spec.ImagePullPolicy != "" {
imagePullPolicy = corev1.PullPolicy(instance.Spec.ImagePullPolicy)
}
pod := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: instance.Namespace,
},
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Pod",
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyNever,
Containers: []corev1.Container{
{
Image: imageName,
ImagePullPolicy: imagePullPolicy,
Name: collectrunner.DeterministicIDForCollector(collector),
Command: []string{"collector"},
Args: []string{
"run",
fmt.Sprintf("--redact=%t", instance.Spec.Redact),
"--collector",
"/troubleshoot/specs/collector.yaml",
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "collector",
MountPath: "/troubleshoot/specs",
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "collector",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: name,
},
},
},
},
},
},
}
if err := controllerutil.SetControllerReference(instance, &pod, r.scheme); err != nil {
return err
}
if err := r.Create(context.Background(), &pod); err != nil {
return err
}
instance.Status.Running = append(instance.Status.Running, collectrunner.DeterministicIDForCollector(collector))
if err := r.Update(context.Background(), instance); err != nil {
return err
}
return nil
}
func contains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
func remove(s []string, r string) []string {
for i, v := range s {
if v == r {
return append(s[:i], s[i+1:]...)
}
}
return s
}

View File

@@ -1,75 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package collectorjob
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"github.com/replicatedhq/troubleshoot/pkg/apis"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -1,25 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package collectorjob
import (
"testing"
)
func TestReconcile(t *testing.T) {
}

View File

@@ -1,34 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"sigs.k8s.io/controller-runtime/pkg/manager"
)
// AddToManagerFuncs is a list of functions to add all Controllers to the Manager
var AddToManagerFuncs []func(manager.Manager) error
// AddToManager adds all Controllers to the Manager
func AddToManager(m manager.Manager) error {
for _, f := range AddToManagerFuncs {
if err := f(m); err != nil {
return err
}
}
return nil
}

View File

@@ -1,96 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package preflight
import (
"context"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("controller")
// Add creates a new Preflight Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcilePreflight{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("preflight-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to Preflight
err = c.Watch(&source.Kind{Type: &troubleshootv1beta1.Preflight{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcilePreflight{}
// ReconcilePreflight reconciles a Preflight object
type ReconcilePreflight struct {
client.Client
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a Preflight object and makes changes based on the state read
// and what is in the Preflight.Spec
// TODO(user): Modify this Reconcile function to implement your Controller logic. The scaffolding writes
// a Deployment as an example
// Automatically generate RBAC rules to allow the Controller to read and write Deployments
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=preflights,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=preflights/status,verbs=get;update;patch
func (r *ReconcilePreflight) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Preflight instance
instance := &troubleshootv1beta1.Preflight{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if kuberneteserrors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}

View File

@@ -1,75 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package preflight
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"github.com/replicatedhq/troubleshoot/pkg/apis"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -1,72 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package preflight
import (
"testing"
"time"
"github.com/onsi/gomega"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
"golang.org/x/net/context"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var c client.Client
var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo", Namespace: "default"}}
var depKey = types.NamespacedName{Name: "foo-deployment", Namespace: "default"}
const timeout = time.Second * 5
func TestReconcile(t *testing.T) {
g := gomega.NewGomegaWithT(t)
instance := &troubleshootv1beta1.Preflight{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}}
// Setup the Manager and Controller. Wrap the Controller Reconcile function so it writes each request to a
// channel when it is finished.
mgr, err := manager.New(cfg, manager.Options{})
g.Expect(err).NotTo(gomega.HaveOccurred())
c = mgr.GetClient()
recFn, requests := SetupTestReconcile(newReconciler(mgr))
g.Expect(add(mgr, recFn)).NotTo(gomega.HaveOccurred())
stopMgr, mgrStopped := StartTestManager(mgr, g)
defer func() {
close(stopMgr)
mgrStopped.Wait()
}()
// Create the Preflight object and expect the Reconcile and Deployment to be created
err = c.Create(context.TODO(), instance)
// The instance object may not be a valid object because it might be missing some required fields.
// Please modify the instance object by adding required fields and then remove the following if statement.
if apierrors.IsInvalid(err) {
t.Logf("failed to create object, got an invalid object error: %v", err)
return
}
g.Expect(err).NotTo(gomega.HaveOccurred())
defer c.Delete(context.TODO(), instance)
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
}

View File

@@ -1,64 +0,0 @@
package preflightjob
import (
"context"
// "fmt"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
)
type AnalysisResult struct {
Success bool
TextOutput string
}
func (r *ReconcilePreflightJob) reconcilePreflightAnalyzers(instance *troubleshootv1beta1.PreflightJob, preflight *troubleshootv1beta1.Preflight) error {
for _, analyzer := range preflight.Spec.Analyzers {
if err := r.reconcileOnePreflightAnalyzer(instance, analyzer); err != nil {
return err
}
}
return nil
}
func (r *ReconcilePreflightJob) reconcileOnePreflightAnalyzer(instance *troubleshootv1beta1.PreflightJob, analyze *troubleshootv1beta1.Analyze) error {
if contains(instance.Status.AnalyzersRunning, idForAnalyzer(analyze)) {
// these are the analyzers we want to attempt to run
if analyze.ClusterVersion != nil {
result, err := r.analyzeClusterVersion(instance, analyze.ClusterVersion)
if err != nil {
return err
}
if result.Success == false {
return nil // collectors are not yet ready
}
instance.Status.AnalyzersSuccessful = append(instance.Status.AnalyzersSuccessful, idForAnalyzer(analyze))
instance.Status.AnalyzersRunning = remove(instance.Status.AnalyzersRunning, idForAnalyzer(analyze))
if err := r.Update(context.Background(), instance); err != nil {
return err
}
return nil
}
return nil
}
return nil
}
func idForAnalyzer(analyzer *troubleshootv1beta1.Analyze) string {
if analyzer.ClusterVersion != nil {
return "cluster-version"
}
if analyzer.StorageClass != nil {
return "storage-classes"
}
return ""
}

View File

@@ -1,11 +0,0 @@
package preflightjob
import (
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
)
func (r *ReconcilePreflightJob) analyzeClusterVersion(instance *troubleshootv1beta1.PreflightJob, clusterVersion *troubleshootv1beta1.ClusterVersion) (*AnalysisResult, error) {
return &AnalysisResult{
Success: true,
}, nil
}

View File

@@ -1,89 +0,0 @@
package preflightjob
import (
"context"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
collectrunner "github.com/replicatedhq/troubleshoot/pkg/collect"
)
func (r *ReconcilePreflightJob) reconcilePreflightCollectors(instance *troubleshootv1beta1.PreflightJob, preflight *troubleshootv1beta1.Preflight) error {
requestedCollectorIDs := make([]string, 0, 0)
for _, collector := range preflight.Spec.Collectors {
requestedCollectorIDs = append(requestedCollectorIDs, collectrunner.DeterministicIDForCollector(collector))
if err := r.reconcileOnePreflightCollector(instance, collector); err != nil {
return err
}
}
if !contains(requestedCollectorIDs, "cluster-info") {
clusterInfo := troubleshootv1beta1.Collect{
ClusterInfo: &troubleshootv1beta1.ClusterInfo{},
}
if err := r.reconcileOnePreflightCollector(instance, &clusterInfo); err != nil {
return err
}
}
if !contains(requestedCollectorIDs, "cluster-resources") {
clusterResources := troubleshootv1beta1.Collect{
ClusterResources: &troubleshootv1beta1.ClusterResources{},
}
if err := r.reconcileOnePreflightCollector(instance, &clusterResources); err != nil {
return err
}
}
return nil
}
func (r *ReconcilePreflightJob) reconcileOnePreflightCollector(instance *troubleshootv1beta1.PreflightJob, collect *troubleshootv1beta1.Collect) error {
if contains(instance.Status.CollectorsRunning, collectrunner.DeterministicIDForCollector(collect)) {
// preflight just leaves these stopped containers.
// it's playing with fire a little, but the analyzers can just
// read from the stdout of the stopped container
//
// in the very common use case (what we are building for today)
// there's not too much risk in something destroying and reaping that stopped pod
// immediately. this is a longer term problem to solve, maybe something,
// the mananger? can broker these collector results. but, ya know...
instance.Status.CollectorsSuccessful = append(instance.Status.CollectorsSuccessful, collectrunner.DeterministicIDForCollector(collect))
instance.Status.CollectorsRunning = remove(instance.Status.CollectorsRunning, collectrunner.DeterministicIDForCollector(collect))
if err := r.Update(context.Background(), instance); err != nil {
return err
}
return nil
}
_, _, err := collectrunner.CreateCollector(r.Client, r.scheme, instance, instance.Name, instance.Namespace, "", "preflight", collect, instance.Spec.Image, instance.Spec.ImagePullPolicy)
if err != nil {
return err
}
instance.Status.CollectorsRunning = append(instance.Status.CollectorsRunning, collectrunner.DeterministicIDForCollector(collect))
if err := r.Update(context.Background(), instance); err != nil {
return err
}
return nil
}
func contains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
func remove(s []string, r string) []string {
for i, v := range s {
if v == r {
return append(s[:i], s[i+1:]...)
}
}
return s
}

View File

@@ -1,178 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package preflightjob
import (
"context"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
"github.com/replicatedhq/troubleshoot/pkg/preflight"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("controller")
// Add creates a new PreflightJob Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcilePreflightJob{
Client: mgr.GetClient(),
config: mgr.GetConfig(),
scheme: mgr.GetScheme(),
}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("preflightjob-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to PreflightJob
err = c.Watch(&source.Kind{Type: &troubleshootv1beta1.PreflightJob{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcilePreflightJob{}
// ReconcilePreflightJob reconciles a PreflightJob object
type ReconcilePreflightJob struct {
client.Client
config *rest.Config
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a PreflightJob object and makes changes based on the state read
// and what is in the PreflightJob.Spec
// TODO(user): Modify this Reconcile function to implement your Controller logic. The scaffolding writes
// a Deployment as an example
// Automatically generate RBAC rules to allow the Controller to read and write Deployments
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=preflightjobs,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=troubleshoot.replicated.com,resources=preflightjobs/status,verbs=get;update;patch
func (r *ReconcilePreflightJob) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the PreflightJob instance
instance := &troubleshootv1beta1.PreflightJob{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if kuberneteserrors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
if !instance.Status.IsServerReady {
preflightServerOptions := preflight.PreflightServerOptions{
ImageName: instance.Spec.Image,
PullPolicy: instance.Spec.ImagePullPolicy,
Name: instance.Name,
Namespace: instance.Namespace,
OwnerReference: instance,
}
pod, _, err := preflight.CreatePreflightServer(r.Client, r.scheme, preflightServerOptions)
if err != nil {
return reconcile.Result{}, err
}
instance.Status.ServerPodName = pod.Name
instance.Status.ServerPodNamespace = pod.Namespace
instance.Status.ServerPodPort = 8000
instance.Status.IsServerReady = true
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
}
namespace := instance.Namespace
if instance.Spec.Preflight.Namespace != "" {
namespace = instance.Spec.Preflight.Namespace
}
preflightSpec, err := r.getPreflightSpec(namespace, instance.Spec.Preflight.Name)
if err != nil {
return reconcile.Result{}, err
}
if len(instance.Status.AnalyzersRunning) == 0 && len(instance.Status.AnalyzersSuccessful) == 0 && len(instance.Status.AnalyzersFailed) == 0 {
// Add them all!
analyzersRunning := []string{}
for _, analyzer := range preflightSpec.Spec.Analyzers {
analyzersRunning = append(analyzersRunning, idForAnalyzer(analyzer))
}
instance.Status.AnalyzersRunning = analyzersRunning
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
}
if err := r.reconcilePreflightCollectors(instance, preflightSpec); err != nil {
return reconcile.Result{}, err
}
if err := r.reconcilePreflightAnalyzers(instance, preflightSpec); err != nil {
return reconcile.Result{}, err
}
// just finished, nothing to do
return reconcile.Result{}, nil
}
func (r *ReconcilePreflightJob) getPreflightSpec(namespace string, name string) (*troubleshootv1beta1.Preflight, error) {
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(r.config)
if err != nil {
return nil, err
}
preflight, err := troubleshootClient.Preflights(namespace).Get(name, metav1.GetOptions{})
if err != nil {
if kuberneteserrors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
return preflight, nil
}

View File

@@ -1,75 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package preflightjob
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"github.com/replicatedhq/troubleshoot/pkg/apis"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -1,25 +0,0 @@
/*
Copyright 2019 Replicated, Inc..
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package preflightjob
import (
"testing"
)
func TestReconcile(t *testing.T) {
}