diff --git a/cmd/helmappcontroller/main.go b/cmd/helmappcontroller/main.go index 8cf88c2d9..2b37531bd 100644 --- a/cmd/helmappcontroller/main.go +++ b/cmd/helmappcontroller/main.go @@ -2,6 +2,10 @@ package main import "C" import ( + "context" + "os" + "time" + rainbondv1alpha1 "github.com/goodrain/rainbond/pkg/apis/rainbond/v1alpha1" "github.com/goodrain/rainbond/pkg/generated/clientset/versioned" k8sutil "github.com/goodrain/rainbond/util/k8s" @@ -9,8 +13,6 @@ import ( "github.com/sirupsen/logrus" k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "os" - "time" ) func init() { @@ -36,19 +38,25 @@ func main() { }, Spec: rainbondv1alpha1.HelmAppSpec{ PreStatus: "", - AppName: "", + AppName: "rainbond-operator", Version: "v1.0.0", Revision: Int32(0), Values: "", + AppStore: &rainbondv1alpha1.HelmAppStore{ + Version: "1111111", + Name: "rainbond", + URL: "https://openchart.goodrain.com/goodrain/rainbond", + }, }, } - if _, err := clientset.RainbondV1alpha1().HelmApps("rbd-system").Create(helmApp); err != nil { + if _, err := clientset.RainbondV1alpha1().HelmApps("rbd-system").Create(context.Background(), + helmApp, metav1.CreateOptions{}); err != nil { if !k8sErrors.IsAlreadyExists(err) { logrus.Fatal(err) } } - ctrl := helmapp.NewController(stopCh, restcfg, 5*time.Second) + ctrl := helmapp.NewController(stopCh, restcfg, 5*time.Second, "/tmp/helm/repo/repositories.yaml", "/tmp/helm/cache") if err = ctrl.Start(); err != nil { logrus.Fatalf("start controller: %v", err) } diff --git a/pkg/apis/rainbond/v1alpha1/helmapp_status.go b/pkg/apis/rainbond/v1alpha1/helmapp_status.go index a4cb939bf..823c0b29f 100644 --- a/pkg/apis/rainbond/v1alpha1/helmapp_status.go +++ b/pkg/apis/rainbond/v1alpha1/helmapp_status.go @@ -26,6 +26,12 @@ func (in *HelmAppStatus) GetCondition(t HelmAppConditionType) (int, *HelmAppCond return -1, nil } +// IsConditionTrue checks if the condition is ready or not based on the given condition type. +func (in *HelmAppStatus) IsConditionTrue(t HelmAppConditionType) bool { + idx, condition := in.GetCondition(t) + return idx != -1 && condition.Status == corev1.ConditionTrue +} + // SetCondition setups the given HelmApp condition. func (in *HelmAppStatus) SetCondition(c HelmAppCondition) { pos, cp := in.GetCondition(c.Type) @@ -40,3 +46,44 @@ func (in *HelmAppStatus) SetCondition(c HelmAppCondition) { in.Conditions = append(in.Conditions, c) } } + +// UpdateCondition updates existing HelmApp condition or creates a new +// one. Sets LastTransitionTime to now if the status has changed. +// Returns true if HelmApp condition has changed or has been added. +func (in *HelmAppStatus) UpdateCondition(condition *HelmAppCondition) bool { + condition.LastTransitionTime = metav1.Now() + // Try to find this HelmApp condition. + conditionIndex, oldCondition := in.GetCondition(condition.Type) + + if oldCondition == nil { + // We are adding new HelmApp condition. + in.Conditions = append(in.Conditions, *condition) + return true + } + + // We are updating an existing condition, so we need to check if it has changed. + if condition.Status == oldCondition.Status { + condition.LastTransitionTime = oldCondition.LastTransitionTime + } + + isEqual := condition.Status == oldCondition.Status && + condition.Reason == oldCondition.Reason && + condition.Message == oldCondition.Message && + condition.LastTransitionTime.Equal(&oldCondition.LastTransitionTime) + + in.Conditions[conditionIndex] = *condition + // Return true if one of the fields have changed. + return !isEqual +} + +func (in *HelmAppStatus) UpdateConditionStatus(conditionType HelmAppConditionType, conditionStatus corev1.ConditionStatus) { + _, condition := in.GetCondition(conditionType) + if condition != nil { + condition.Status = conditionStatus + in.UpdateCondition(condition) + return + } + + condition = NewHelmAppCondition(conditionType, conditionStatus, "", "") + in.UpdateCondition(condition) +} diff --git a/pkg/apis/rainbond/v1alpha1/helmapp_types.go b/pkg/apis/rainbond/v1alpha1/helmapp_types.go index d6e7762ec..3e7ae09b4 100644 --- a/pkg/apis/rainbond/v1alpha1/helmapp_types.go +++ b/pkg/apis/rainbond/v1alpha1/helmapp_types.go @@ -62,8 +62,10 @@ type HelmAppConditionType string // These are valid conditions of helm app. const ( - // HelmAppDetected indicates whether the helm app has been detected. - HelmAppDetected HelmAppConditionType = "Detected" + // HelmAppRepoReady indicates whether the helm repository is ready. + HelmAppRepoReady HelmAppConditionType = "RepoReady" + // HelmAppChartReady indicates whether the chart is ready. + HelmAppChartReady HelmAppConditionType = "ChartReady" // HelmAppDetected indicates whether the helm app has been installed. HelmAppInstalled HelmAppConditionType = "Installed" ) @@ -109,6 +111,10 @@ type HelmAppSpec struct { // The values.yaml of the helm app, encoded by base64. Values string `json:"values"` + + // The helm app store. + // TODO: validation. not null + AppStore *HelmAppStore `json:"appStore"` } // HelmAppStore represents a helm repo. diff --git a/pkg/apis/rainbond/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rainbond/v1alpha1/zz_generated.deepcopy.go index 665612384..2b5df780f 100644 --- a/pkg/apis/rainbond/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/rainbond/v1alpha1/zz_generated.deepcopy.go @@ -109,6 +109,11 @@ func (in *HelmAppSpec) DeepCopyInto(out *HelmAppSpec) { *out = new(int32) **out = **in } + if in.AppStore != nil { + in, out := &in.AppStore, &out.AppStore + *out = new(HelmAppStore) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmAppSpec. diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go index 57eafd5a6..b6cd74c83 100644 --- a/pkg/generated/clientset/versioned/clientset.go +++ b/pkg/generated/clientset/versioned/clientset.go @@ -61,7 +61,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { configShallowCopy := *c if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { if configShallowCopy.Burst <= 0 { - return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") } configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) } diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index 25ad0faa9..14bc99f21 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -31,7 +31,7 @@ import ( var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) -var parameterCodec = runtime.NewParameterCodec(scheme) + var localSchemeBuilder = runtime.SchemeBuilder{ rainbondv1alpha1.AddToScheme, } diff --git a/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_helmapp.go b/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_helmapp.go index 2bfdb1bd0..b99a0797d 100644 --- a/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_helmapp.go +++ b/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_helmapp.go @@ -21,6 +21,8 @@ package fake import ( + "context" + v1alpha1 "github.com/goodrain/rainbond/pkg/apis/rainbond/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" labels "k8s.io/apimachinery/pkg/labels" @@ -41,7 +43,7 @@ var helmappsResource = schema.GroupVersionResource{Group: "rainbond.goodrain.io" var helmappsKind = schema.GroupVersionKind{Group: "rainbond.goodrain.io", Version: "v1alpha1", Kind: "HelmApp"} // Get takes name of the helmApp, and returns the corresponding helmApp object, and an error if there is any. -func (c *FakeHelmApps) Get(name string, options v1.GetOptions) (result *v1alpha1.HelmApp, err error) { +func (c *FakeHelmApps) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HelmApp, err error) { obj, err := c.Fake. Invokes(testing.NewGetAction(helmappsResource, c.ns, name), &v1alpha1.HelmApp{}) @@ -52,7 +54,7 @@ func (c *FakeHelmApps) Get(name string, options v1.GetOptions) (result *v1alpha1 } // List takes label and field selectors, and returns the list of HelmApps that match those selectors. -func (c *FakeHelmApps) List(opts v1.ListOptions) (result *v1alpha1.HelmAppList, err error) { +func (c *FakeHelmApps) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HelmAppList, err error) { obj, err := c.Fake. Invokes(testing.NewListAction(helmappsResource, helmappsKind, c.ns, opts), &v1alpha1.HelmAppList{}) @@ -74,14 +76,14 @@ func (c *FakeHelmApps) List(opts v1.ListOptions) (result *v1alpha1.HelmAppList, } // Watch returns a watch.Interface that watches the requested helmApps. -func (c *FakeHelmApps) Watch(opts v1.ListOptions) (watch.Interface, error) { +func (c *FakeHelmApps) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewWatchAction(helmappsResource, c.ns, opts)) } // Create takes the representation of a helmApp and creates it. Returns the server's representation of the helmApp, and an error, if there is any. -func (c *FakeHelmApps) Create(helmApp *v1alpha1.HelmApp) (result *v1alpha1.HelmApp, err error) { +func (c *FakeHelmApps) Create(ctx context.Context, helmApp *v1alpha1.HelmApp, opts v1.CreateOptions) (result *v1alpha1.HelmApp, err error) { obj, err := c.Fake. Invokes(testing.NewCreateAction(helmappsResource, c.ns, helmApp), &v1alpha1.HelmApp{}) @@ -92,7 +94,7 @@ func (c *FakeHelmApps) Create(helmApp *v1alpha1.HelmApp) (result *v1alpha1.HelmA } // Update takes the representation of a helmApp and updates it. Returns the server's representation of the helmApp, and an error, if there is any. -func (c *FakeHelmApps) Update(helmApp *v1alpha1.HelmApp) (result *v1alpha1.HelmApp, err error) { +func (c *FakeHelmApps) Update(ctx context.Context, helmApp *v1alpha1.HelmApp, opts v1.UpdateOptions) (result *v1alpha1.HelmApp, err error) { obj, err := c.Fake. Invokes(testing.NewUpdateAction(helmappsResource, c.ns, helmApp), &v1alpha1.HelmApp{}) @@ -104,7 +106,7 @@ func (c *FakeHelmApps) Update(helmApp *v1alpha1.HelmApp) (result *v1alpha1.HelmA // UpdateStatus was generated because the type contains a Status member. // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeHelmApps) UpdateStatus(helmApp *v1alpha1.HelmApp) (*v1alpha1.HelmApp, error) { +func (c *FakeHelmApps) UpdateStatus(ctx context.Context, helmApp *v1alpha1.HelmApp, opts v1.UpdateOptions) (*v1alpha1.HelmApp, error) { obj, err := c.Fake. Invokes(testing.NewUpdateSubresourceAction(helmappsResource, "status", c.ns, helmApp), &v1alpha1.HelmApp{}) @@ -115,7 +117,7 @@ func (c *FakeHelmApps) UpdateStatus(helmApp *v1alpha1.HelmApp) (*v1alpha1.HelmAp } // Delete takes name of the helmApp and deletes it. Returns an error if one occurs. -func (c *FakeHelmApps) Delete(name string, options *v1.DeleteOptions) error { +func (c *FakeHelmApps) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { _, err := c.Fake. Invokes(testing.NewDeleteAction(helmappsResource, c.ns, name), &v1alpha1.HelmApp{}) @@ -123,15 +125,15 @@ func (c *FakeHelmApps) Delete(name string, options *v1.DeleteOptions) error { } // DeleteCollection deletes a collection of objects. -func (c *FakeHelmApps) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(helmappsResource, c.ns, listOptions) +func (c *FakeHelmApps) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(helmappsResource, c.ns, listOpts) _, err := c.Fake.Invokes(action, &v1alpha1.HelmAppList{}) return err } // Patch applies the patch and returns the patched helmApp. -func (c *FakeHelmApps) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.HelmApp, err error) { +func (c *FakeHelmApps) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HelmApp, err error) { obj, err := c.Fake. Invokes(testing.NewPatchSubresourceAction(helmappsResource, c.ns, name, pt, data, subresources...), &v1alpha1.HelmApp{}) diff --git a/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/helmapp.go b/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/helmapp.go index 17b5ab5ee..fa4a6594b 100644 --- a/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/helmapp.go +++ b/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/helmapp.go @@ -21,6 +21,7 @@ package v1alpha1 import ( + "context" "time" v1alpha1 "github.com/goodrain/rainbond/pkg/apis/rainbond/v1alpha1" @@ -39,15 +40,15 @@ type HelmAppsGetter interface { // HelmAppInterface has methods to work with HelmApp resources. type HelmAppInterface interface { - Create(*v1alpha1.HelmApp) (*v1alpha1.HelmApp, error) - Update(*v1alpha1.HelmApp) (*v1alpha1.HelmApp, error) - UpdateStatus(*v1alpha1.HelmApp) (*v1alpha1.HelmApp, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.HelmApp, error) - List(opts v1.ListOptions) (*v1alpha1.HelmAppList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.HelmApp, err error) + Create(ctx context.Context, helmApp *v1alpha1.HelmApp, opts v1.CreateOptions) (*v1alpha1.HelmApp, error) + Update(ctx context.Context, helmApp *v1alpha1.HelmApp, opts v1.UpdateOptions) (*v1alpha1.HelmApp, error) + UpdateStatus(ctx context.Context, helmApp *v1alpha1.HelmApp, opts v1.UpdateOptions) (*v1alpha1.HelmApp, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.HelmApp, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.HelmAppList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HelmApp, err error) HelmAppExpansion } @@ -66,20 +67,20 @@ func newHelmApps(c *RainbondV1alpha1Client, namespace string) *helmApps { } // Get takes name of the helmApp, and returns the corresponding helmApp object, and an error if there is any. -func (c *helmApps) Get(name string, options v1.GetOptions) (result *v1alpha1.HelmApp, err error) { +func (c *helmApps) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HelmApp, err error) { result = &v1alpha1.HelmApp{} err = c.client.Get(). Namespace(c.ns). Resource("helmapps"). Name(name). VersionedParams(&options, scheme.ParameterCodec). - Do(). + Do(ctx). Into(result) return } // List takes label and field selectors, and returns the list of HelmApps that match those selectors. -func (c *helmApps) List(opts v1.ListOptions) (result *v1alpha1.HelmAppList, err error) { +func (c *helmApps) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HelmAppList, err error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second @@ -90,13 +91,13 @@ func (c *helmApps) List(opts v1.ListOptions) (result *v1alpha1.HelmAppList, err Resource("helmapps"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). - Do(). + Do(ctx). Into(result) return } // Watch returns a watch.Interface that watches the requested helmApps. -func (c *helmApps) Watch(opts v1.ListOptions) (watch.Interface, error) { +func (c *helmApps) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second @@ -107,87 +108,90 @@ func (c *helmApps) Watch(opts v1.ListOptions) (watch.Interface, error) { Resource("helmapps"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). - Watch() + Watch(ctx) } // Create takes the representation of a helmApp and creates it. Returns the server's representation of the helmApp, and an error, if there is any. -func (c *helmApps) Create(helmApp *v1alpha1.HelmApp) (result *v1alpha1.HelmApp, err error) { +func (c *helmApps) Create(ctx context.Context, helmApp *v1alpha1.HelmApp, opts v1.CreateOptions) (result *v1alpha1.HelmApp, err error) { result = &v1alpha1.HelmApp{} err = c.client.Post(). Namespace(c.ns). Resource("helmapps"). + VersionedParams(&opts, scheme.ParameterCodec). Body(helmApp). - Do(). + Do(ctx). Into(result) return } // Update takes the representation of a helmApp and updates it. Returns the server's representation of the helmApp, and an error, if there is any. -func (c *helmApps) Update(helmApp *v1alpha1.HelmApp) (result *v1alpha1.HelmApp, err error) { +func (c *helmApps) Update(ctx context.Context, helmApp *v1alpha1.HelmApp, opts v1.UpdateOptions) (result *v1alpha1.HelmApp, err error) { result = &v1alpha1.HelmApp{} err = c.client.Put(). Namespace(c.ns). Resource("helmapps"). Name(helmApp.Name). + VersionedParams(&opts, scheme.ParameterCodec). Body(helmApp). - Do(). + Do(ctx). Into(result) return } // UpdateStatus was generated because the type contains a Status member. // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *helmApps) UpdateStatus(helmApp *v1alpha1.HelmApp) (result *v1alpha1.HelmApp, err error) { +func (c *helmApps) UpdateStatus(ctx context.Context, helmApp *v1alpha1.HelmApp, opts v1.UpdateOptions) (result *v1alpha1.HelmApp, err error) { result = &v1alpha1.HelmApp{} err = c.client.Put(). Namespace(c.ns). Resource("helmapps"). Name(helmApp.Name). SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). Body(helmApp). - Do(). + Do(ctx). Into(result) return } // Delete takes name of the helmApp and deletes it. Returns an error if one occurs. -func (c *helmApps) Delete(name string, options *v1.DeleteOptions) error { +func (c *helmApps) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { return c.client.Delete(). Namespace(c.ns). Resource("helmapps"). Name(name). - Body(options). - Do(). + Body(&opts). + Do(ctx). Error() } // DeleteCollection deletes a collection of objects. -func (c *helmApps) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { +func (c *helmApps) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second } return c.client.Delete(). Namespace(c.ns). Resource("helmapps"). - VersionedParams(&listOptions, scheme.ParameterCodec). + VersionedParams(&listOpts, scheme.ParameterCodec). Timeout(timeout). - Body(options). - Do(). + Body(&opts). + Do(ctx). Error() } // Patch applies the patch and returns the patched helmApp. -func (c *helmApps) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.HelmApp, err error) { +func (c *helmApps) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HelmApp, err error) { result = &v1alpha1.HelmApp{} err = c.client.Patch(pt). Namespace(c.ns). Resource("helmapps"). - SubResource(subresources...). Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). Body(data). - Do(). + Do(ctx). Into(result) return } diff --git a/pkg/generated/informers/externalversions/rainbond/v1alpha1/helmapp.go b/pkg/generated/informers/externalversions/rainbond/v1alpha1/helmapp.go index 2431e9972..64a7a6789 100644 --- a/pkg/generated/informers/externalversions/rainbond/v1alpha1/helmapp.go +++ b/pkg/generated/informers/externalversions/rainbond/v1alpha1/helmapp.go @@ -21,6 +21,7 @@ package v1alpha1 import ( + "context" time "time" rainbondv1alpha1 "github.com/goodrain/rainbond/pkg/apis/rainbond/v1alpha1" @@ -63,13 +64,13 @@ func NewFilteredHelmAppInformer(client versioned.Interface, namespace string, re if tweakListOptions != nil { tweakListOptions(&options) } - return client.RainbondV1alpha1().HelmApps(namespace).List(options) + return client.RainbondV1alpha1().HelmApps(namespace).List(context.TODO(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.RainbondV1alpha1().HelmApps(namespace).Watch(options) + return client.RainbondV1alpha1().HelmApps(namespace).Watch(context.TODO(), options) }, }, &rainbondv1alpha1.HelmApp{}, diff --git a/pkg/generated/listers/rainbond/v1alpha1/helmapp.go b/pkg/generated/listers/rainbond/v1alpha1/helmapp.go index eeafa56bd..03e5bfcf2 100644 --- a/pkg/generated/listers/rainbond/v1alpha1/helmapp.go +++ b/pkg/generated/listers/rainbond/v1alpha1/helmapp.go @@ -28,8 +28,10 @@ import ( ) // HelmAppLister helps list HelmApps. +// All objects returned here must be treated as read-only. type HelmAppLister interface { // List lists all HelmApps in the indexer. + // Objects returned here must be treated as read-only. List(selector labels.Selector) (ret []*v1alpha1.HelmApp, err error) // HelmApps returns an object that can list and get HelmApps. HelmApps(namespace string) HelmAppNamespaceLister @@ -60,10 +62,13 @@ func (s *helmAppLister) HelmApps(namespace string) HelmAppNamespaceLister { } // HelmAppNamespaceLister helps list and get HelmApps. +// All objects returned here must be treated as read-only. type HelmAppNamespaceLister interface { // List lists all HelmApps in the indexer for a given namespace. + // Objects returned here must be treated as read-only. List(selector labels.Selector) (ret []*v1alpha1.HelmApp, err error) // Get retrieves the HelmApp from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. Get(name string) (*v1alpha1.HelmApp, error) HelmAppNamespaceListerExpansion } diff --git a/util/k8s/k8s.go b/util/k8s/k8s.go index a2c64eeb7..52af85767 100644 --- a/util/k8s/k8s.go +++ b/util/k8s/k8s.go @@ -8,8 +8,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" diff --git a/worker/controllers/helmapp/controller.go b/worker/controllers/helmapp/controller.go index 3f2865736..22fe5596b 100644 --- a/worker/controllers/helmapp/controller.go +++ b/worker/controllers/helmapp/controller.go @@ -16,12 +16,13 @@ type Controller struct { controlLoop *ControlLoop } -func NewController(stopCh chan struct{}, restcfg *rest.Config, resyncPeriod time.Duration) *Controller { +func NewController(stopCh chan struct{}, restcfg *rest.Config, resyncPeriod time.Duration, + repoFile, repoCache string) *Controller { queue := workqueue.New() clientset := versioned.NewForConfigOrDie(restcfg) storer := NewStorer(clientset, resyncPeriod, queue) - controlLoop := NewControlLoop(storer, queue) + controlLoop := NewControlLoop(clientset, storer, queue, repoFile, repoCache) return &Controller{ storer: storer, diff --git a/worker/controllers/helmapp/controlloop.go b/worker/controllers/helmapp/controlloop.go index 0012cb1e4..612623de2 100644 --- a/worker/controllers/helmapp/controlloop.go +++ b/worker/controllers/helmapp/controlloop.go @@ -1,6 +1,10 @@ package helmapp import ( + "context" + "github.com/goodrain/rainbond/pkg/generated/clientset/versioned" + "github.com/goodrain/rainbond/worker/controllers/helmapp/helm" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "strings" "github.com/goodrain/rainbond/pkg/apis/rainbond/v1alpha1" @@ -10,17 +14,26 @@ import ( ) type ControlLoop struct { + clientset versioned.Interface storer Storer workqueue workqueue.Interface + repo *helm.Repo } // NewControlLoop - -func NewControlLoop(storer Storer, +func NewControlLoop(clientset versioned.Interface, + storer Storer, workqueue workqueue.Interface, + repoFile string, + repoCache string, ) *ControlLoop { + repo := helm.NewRepo(repoFile, repoCache) + return &ControlLoop{ + clientset: clientset, storer: storer, workqueue: workqueue, + repo: repo, } } @@ -61,8 +74,20 @@ func (c *ControlLoop) Reconcile(helmApp *v1alpha1.HelmApp) error { status := NewStatus(helmApp.Status) - detector := NewDetector(status) - detector.Detect() + detector := NewDetector(helmApp, status, c.repo) + err := detector.Detect() + if err != nil { + // TODO: create event + return err + } + + helmApp.Status = status.HelmAppStatus + // TODO: context + if _, err := c.clientset.RainbondV1alpha1().HelmApps(helmApp.Namespace). + UpdateStatus(context.Background(), helmApp, metav1.UpdateOptions{}); err != nil { + // TODO: create event + return err + } return nil } diff --git a/worker/controllers/helmapp/detector.go b/worker/controllers/helmapp/detector.go index 6573cf535..69bfb5314 100644 --- a/worker/controllers/helmapp/detector.go +++ b/worker/controllers/helmapp/detector.go @@ -1,12 +1,22 @@ package helmapp +import ( + "github.com/goodrain/rainbond/pkg/apis/rainbond/v1alpha1" + "github.com/goodrain/rainbond/worker/controllers/helmapp/helm" + corev1 "k8s.io/api/core/v1" +) + type Detector struct { - status *Status + helmApp *v1alpha1.HelmApp + status *Status + repo *helm.Repo } -func NewDetector(status *Status) *Detector { +func NewDetector(helmApp *v1alpha1.HelmApp, status *Status, repo *helm.Repo) *Detector { return &Detector{ - status: status, + helmApp: helmApp, + status: status, + repo: repo, } } @@ -16,6 +26,15 @@ func (d *Detector) Detect() error { } // add repo + if !d.status.IsConditionTrue(v1alpha1.HelmAppRepoReady) { + appStore := d.helmApp.Spec.AppStore + if err := d.repo.Add(appStore.Name, appStore.URL, "", ""); err != nil { + d.status.SetCondition(*v1alpha1.NewHelmAppCondition( + v1alpha1.HelmAppRepoReady, corev1.ConditionFalse, "RepoFailed", err.Error())) + return err + } + d.status.UpdateConditionStatus(v1alpha1.HelmAppRepoReady, corev1.ConditionTrue) + } return nil } diff --git a/worker/controllers/helmapp/helm/repo.go b/worker/controllers/helmapp/helm/repo.go index 55beee981..129693ab6 100644 --- a/worker/controllers/helmapp/helm/repo.go +++ b/worker/controllers/helmapp/helm/repo.go @@ -27,33 +27,24 @@ var deprecatedRepos = map[string]string{ // Repo - type Repo struct { - name string - url string - username string - password string - forceUpdate bool - repoFile string repoCache string + forceUpdate bool insecureSkipTLSverify bool } -func NewRepo(name, url, username, password, repoFile, repoCache string) *Repo { +// NewRepo creates a new repo. +func NewRepo(repoFile, repoCache string) *Repo { return &Repo{ - name: name, - url: url, - username: username, - password: password, - forceUpdate: true, - repoFile: repoFile, - repoCache: repoCache, + repoFile: repoFile, + repoCache: repoCache, } } -func (o *Repo) Add() error { +func (o *Repo) Add(name, url, username, password string) error { var buf bytes.Buffer - err := o.add(&buf) + err := o.add(&buf, name, url, username, password) if err != nil { return err } @@ -64,11 +55,11 @@ func (o *Repo) Add() error { return nil } -func (o *Repo) add(out io.Writer) error { +func (o *Repo) add(out io.Writer, name, url, username, password string) error { // Block deprecated repos for oldURL, newURL := range deprecatedRepos { - if strings.Contains(o.url, oldURL) { - return fmt.Errorf("repo %q is no longer available; try %q instead", o.url, newURL) + if strings.Contains(url, oldURL) { + return fmt.Errorf("repo %q is no longer available; try %q instead", url, newURL) } } @@ -101,27 +92,27 @@ func (o *Repo) add(out io.Writer) error { } c := repo.Entry{ - Name: o.name, - URL: o.url, - Username: o.username, - Password: o.password, + Name: name, + URL: url, + Username: username, + Password: password, InsecureSkipTLSverify: o.insecureSkipTLSverify, } // If the repo exists do one of two things: // 1. If the configuration for the name is the same continue without error // 2. When the config is different require --force-update - if !o.forceUpdate && f.Has(o.name) { - existing := f.Get(o.name) + if !o.forceUpdate && f.Has(name) { + existing := f.Get(name) if c != *existing { // The input coming in for the name is different from what is already // configured. Return an error. - return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) + return errors.Errorf("repository name (%s) already exists, please specify a different name", name) } // The add is idempotent so do nothing - fmt.Fprintf(out, "%q already exists with the same configuration, skipping\n", o.name) + fmt.Fprintf(out, "%q already exists with the same configuration, skipping\n", name) return nil } @@ -137,7 +128,7 @@ func (o *Repo) add(out io.Writer) error { r.CachePath = o.repoCache } if _, err := r.DownloadIndexFile(); err != nil { - return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", o.url) + return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url) } f.Update(&c) @@ -145,6 +136,6 @@ func (o *Repo) add(out io.Writer) error { if err := f.WriteFile(o.repoFile, 0644); err != nil { return err } - fmt.Fprintf(out, "%q has been added to your repositories\n", o.name) + fmt.Fprintf(out, "%q has been added to your repositories\n", name) return nil } diff --git a/worker/controllers/helmapp/helm/repo_test.go b/worker/controllers/helmapp/helm/repo_test.go index 63140905c..432929fc6 100644 --- a/worker/controllers/helmapp/helm/repo_test.go +++ b/worker/controllers/helmapp/helm/repo_test.go @@ -7,11 +7,9 @@ import ( ) func TestRepoAdd(t *testing.T) { - repo := NewRepo(util.NewUUID(), - "https://openchart.goodrain.com/goodrain/rainbond", - "", "", + repo := NewRepo( "/tmp/helm/repo/repositories.yaml", "/tmp/helm/cache") - err := repo.Add() + err := repo.Add(util.NewUUID(), "https://openchart.goodrain.com/goodrain/rainbond", "", "") assert.Nil(t, err) } diff --git a/worker/controllers/helmapp/status.go b/worker/controllers/helmapp/status.go index cae141694..415544bb2 100644 --- a/worker/controllers/helmapp/status.go +++ b/worker/controllers/helmapp/status.go @@ -11,16 +11,7 @@ type Status struct { // NewStatus creates a new helm app status. func NewStatus(status v1alpha1.HelmAppStatus) *Status { - idx, _ := status.GetCondition(v1alpha1.HelmAppDetected) - if idx == -1 { - status.SetCondition(*v1alpha1.NewHelmAppCondition( - v1alpha1.HelmAppDetected, - corev1.ConditionFalse, - "", - "", - )) - } - idx, _ = status.GetCondition(v1alpha1.HelmAppInstalled) + idx, _ := status.GetCondition(v1alpha1.HelmAppInstalled) if idx == -1 { status.SetCondition(*v1alpha1.NewHelmAppCondition( v1alpha1.HelmAppInstalled, @@ -35,6 +26,14 @@ func NewStatus(status v1alpha1.HelmAppStatus) *Status { } func (s *Status) isDetected() bool { - idx, condition := s.GetCondition(v1alpha1.HelmAppDetected) - return idx != -1 && condition.Status == corev1.ConditionTrue + types := []v1alpha1.HelmAppConditionType{ + v1alpha1.HelmAppRepoReady, + v1alpha1.HelmAppChartReady, + } + for _, t := range types { + if !s.IsConditionTrue(t) { + return false + } + } + return true }