Skip to content

Commit

Permalink
Refactors TektonPipeline Reconciler
Browse files Browse the repository at this point in the history
Signed-off-by: Shivam Mukhade <[email protected]>
  • Loading branch information
sm43 committed Aug 18, 2021
1 parent b9c72c7 commit f261e40
Show file tree
Hide file tree
Showing 16 changed files with 1,093 additions and 63 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/manifestival/controller-runtime-client v0.4.0
github.com/manifestival/manifestival v0.7.0
github.com/markbates/inflect v1.0.4
github.com/mitchellh/hashstructure v1.1.0
github.com/tektoncd/plumbing v0.0.0-20210514044347-f8a9689d5bd5
github.com/tektoncd/triggers v0.14.1
go.uber.org/zap v1.16.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,8 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=
github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
Expand Down
101 changes: 98 additions & 3 deletions pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,25 @@ import (
"knative.dev/pkg/apis"
)

const (
CrdInstalled apis.ConditionType = "CrdInstalled"
ClustersScoped apis.ConditionType = "ClusterScopedInstalled"
NamespaceScoped apis.ConditionType = "NamespaceScopedInstalled"
DeploymentsInstalled apis.ConditionType = "DeploymentInstalled"
WebhookReady apis.ConditionType = "WebhookReady"
ControllerReady apis.ConditionType = "ControllerReady"
)

var (
_ TektonComponentStatus = (*TektonPipelineStatus)(nil)

pipelineCondSet = apis.NewLivingConditionSet(
DependenciesInstalled,
DeploymentsAvailable,
InstallSucceeded,
CrdInstalled,
ClustersScoped,
NamespaceScoped,
DeploymentsInstalled,
WebhookReady,
ControllerReady,
)
)

Expand All @@ -51,6 +63,10 @@ func (tps *TektonPipelineStatus) IsReady() bool {
return pipelineCondSet.Manage(tps).IsHappy()
}

func (tps *TektonPipelineStatus) MarkReady() {
pipelineCondSet.Manage(tps).MarkTrue(apis.ConditionReady)
}

// MarkInstallSucceeded marks the InstallationSucceeded status as true.
func (tps *TektonPipelineStatus) MarkInstallSucceeded() {
pipelineCondSet.Manage(tps).MarkTrue(InstallSucceeded)
Expand All @@ -60,6 +76,85 @@ func (tps *TektonPipelineStatus) MarkInstallSucceeded() {
}
}

func (tps *TektonPipelineStatus) MarkCRDsInstalled() {
pipelineCondSet.Manage(tps).MarkTrue(CrdInstalled)
}

func (tps *TektonPipelineStatus) MarkClustersScopedResourcesInstalled() {
pipelineCondSet.Manage(tps).MarkTrue(ClustersScoped)
}

func (tps *TektonPipelineStatus) MarkNamespaceScopedResourcesInstalled() {
pipelineCondSet.Manage(tps).MarkTrue(NamespaceScoped)
}

func (tps *TektonPipelineStatus) MarkDeploymentsInstalled() {
pipelineCondSet.Manage(tps).MarkTrue(DeploymentsInstalled)
}

func (tps *TektonPipelineStatus) MarkWebhookReady() {
pipelineCondSet.Manage(tps).MarkTrue(WebhookReady)
}

func (tps *TektonPipelineStatus) MarkControllerReady() {
pipelineCondSet.Manage(tps).MarkTrue(ControllerReady)
}

func (tps *TektonPipelineStatus) MarkNotReady(msg string) {
pipelineCondSet.Manage(tps).MarkFalse(
apis.ConditionReady,
"Error",
"Ready: %s", msg)
}

func (tps *TektonPipelineStatus) MarkCRDsInstallationFailed(msg string) {
tps.MarkNotReady("CRDs installation failed")
pipelineCondSet.Manage(tps).MarkFalse(
CrdInstalled,
"Error",
"Install failed with message: %s", msg)
}

func (tps *TektonPipelineStatus) MarkClustersScopedInstallationFailed(msg string) {
tps.MarkNotReady("Cluster Scoped resources installation failed")
pipelineCondSet.Manage(tps).MarkFalse(
ClustersScoped,
"Error",
"Install failed with message: %s", msg)
}

func (tps *TektonPipelineStatus) MarkNamespaceScopedInstallationFailed(msg string) {
tps.MarkNotReady("Namespace Scoped resources installation failed")
pipelineCondSet.Manage(tps).MarkFalse(
NamespaceScoped,
"Error",
"Install failed with message: %s", msg)
}

func (tps *TektonPipelineStatus) MarkDeploymentInstallationFailed(msg string) {
tps.MarkNotReady("Deployment resources installation failed")
pipelineCondSet.Manage(tps).MarkFalse(
DeploymentsInstalled,
"Error",
"Install failed with message: %s", msg)
}

func (tps *TektonPipelineStatus) MarkWebhookNotAvailable(msg string) {
tps.MarkNotReady("Webhook Deployment not available ")
pipelineCondSet.Manage(tps).MarkFalse(
WebhookReady,
"Error",
"Webhook: %s", msg)
}

func (tps *TektonPipelineStatus) MarkControllerNotAvailable(msg string) {
tps.MarkNotReady("Controller Deployment not available ")
pipelineCondSet.Manage(tps).MarkFalse(
ControllerReady,
"Error",
"Controller: %s", msg)
}

// MarkInstallFailed marks the InstallationSucceeded status as false with the given
// message.
func (tps *TektonPipelineStatus) MarkInstallFailed(msg string) {
Expand Down
8 changes: 7 additions & 1 deletion pkg/apis/operator/v1alpha1/tektonpipeline_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package v1alpha1

import (
"reflect"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
duckv1 "knative.dev/pkg/apis/duck/v1"
)
Expand Down Expand Up @@ -61,7 +63,6 @@ type TektonPipelineSpec struct {
// TektonPipelineStatus defines the observed state of TektonPipeline
type TektonPipelineStatus struct {
duckv1.Status `json:",inline"`

// The version of the installed release
// +optional
Version string `json:"version,omitempty"`
Expand All @@ -71,6 +72,11 @@ type TektonPipelineStatus struct {
Manifests []string `json:"manifests,omitempty"`
}

// IsInitialized checks if Status is intitialized
func (t TektonPipelineStatus) IsInitialized() bool {
return !reflect.DeepEqual(t, TektonPipelineStatus{})
}

// TektonPipelineList contains a list of TektonPipeline
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type TektonPipelineList struct {
Expand Down
169 changes: 169 additions & 0 deletions pkg/reconciler/installer/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
Copyright 2021 The Tekton Authors
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 installer

import (
"errors"
"strings"

mf "github.com/manifestival/manifestival"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
)

var (
namespacePred = mf.ByKind("Namespace")
configMapPred = mf.ByKind("ConfigMap")
secretPred = mf.ByKind("Secret")
deploymentPred = mf.ByKind("Deployment")
servicePred = mf.ByKind("Service")
serviceAccountPred = mf.ByKind("ServiceAccount")
rolePred = mf.ByKind("Role")
roleBindingPred = mf.ByKind("RoleBinding")
clusterRolePred = mf.ByKind("ClusterRole")
clusterRoleBindingPred = mf.ByKind("ClusterRoleBinding")
podSecurityPolicyPred = mf.ByKind("PodSecurityPolicy")
validatingWebhookConfigurationPred = mf.ByKind("ValidatingWebhookConfiguration")
mutatingWebhookConfigurationPred = mf.ByKind("MutatingWebhookConfiguration")
horizontalPodAutoscalerPred = mf.ByKind("HorizontalPodAutoscaler")
)

type Installer struct {
Manifest mf.Manifest
Label string
}

func (i *Installer) EnsureCRDs() error {
if err := i.IsInstalled(mf.CRDs); err != nil {
return err
}
return nil
}

func (i *Installer) EnsureClusterScopedResources() error {
if err := i.IsInstalled(
namespacePred,
clusterRolePred,
podSecurityPolicyPred,
validatingWebhookConfigurationPred,
mutatingWebhookConfigurationPred,
); err != nil {
return err
}
return nil
}

func (i *Installer) EnsureNamespaceScopedResources() error {

if err := i.IsInstalled(
serviceAccountPred,
clusterRoleBindingPred,
rolePred,
roleBindingPred,
configMapPred,
secretPred,
horizontalPodAutoscalerPred,
); err != nil {
return err
}

return nil
}

func (i *Installer) EnsureDeploymentResources() error {

if err := i.IsInstalled(
deploymentPred,
servicePred,
); err != nil {
return err
}

return nil
}

func (i *Installer) IsInstalled(pred ...mf.Predicate) error {

manifest := i.Manifest.Filter(mf.Any(pred...))
if len(manifest.Resources()) == 0 {
return nil
}

for _, res := range manifest.Resources() {

// Check if resource exist
_, err := i.Manifest.Client.Get(&res)
if err == nil {
// If resource exist then continue
// TODO: compare both objects to decide update or not
// TODO: check label to validate if version is changed
continue
}

// If resource doesn't exist then create
err = i.Manifest.Client.Create(&res)
if err != nil {
return err
}
}

return nil
}

func (i *Installer) IsWebhookReady() error {
return i.IsDeploymentReady("webhook")
}

func (i *Installer) IsControllerReady() error {
return i.IsDeploymentReady("controller")
}

func (i *Installer) IsDeploymentReady(name string) error {

for _, u := range i.Manifest.Filter(deploymentPred).Resources() {

if !strings.Contains(u.GetName(), name) {
continue
}

resource, err := i.Manifest.Client.Get(&u)
if err != nil {
return err
}

deployment := &appsv1.Deployment{}
err = runtime.DefaultUnstructuredConverter.FromUnstructured(resource.Object, deployment)
if err != nil {
return err
}

if !isDeploymentAvailable(deployment) {
return errors.New("deployment not available")
}
}

return nil
}
func isDeploymentAvailable(d *appsv1.Deployment) bool {
for _, c := range d.Status.Conditions {
if c.Type == appsv1.DeploymentAvailable && c.Status == corev1.ConditionTrue {
return true
}
}
return false
}
Loading

0 comments on commit f261e40

Please sign in to comment.