Skip to content

Commit

Permalink
Updates TektonPipeline to use TektonInstallerSet
Browse files Browse the repository at this point in the history
This updates the reconciler to use TektonInstallerSet to install
the pipelines component. If version is changed or target namespace is
changed then previous TektonInstallerSet will get deleted and a new
one will be created with required spec.

Signed-off-by: Shivam Mukhade <[email protected]>
  • Loading branch information
Shivam Mukhade committed Sep 2, 2021
1 parent ae18190 commit 811229d
Show file tree
Hide file tree
Showing 9 changed files with 445 additions and 237 deletions.
2 changes: 1 addition & 1 deletion pkg/apis/operator/v1alpha1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const (
InstallSucceeded apis.ConditionType = "InstallSucceeded"
// DeploymentsAvailable is a Condition indicating whether or not the Deployments of
// the respective component have come up successfully.
DeploymentsAvailable apis.ConditionType = "DeploymentsAvailable"
// DeploymentsAvailable apis.ConditionType = "DeploymentsAvailable"
)

// TektonComponent is a common interface for accessing meta, spec and status of all known types.
Expand Down
152 changes: 98 additions & 54 deletions pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,111 +17,155 @@ limitations under the License.
package v1alpha1

import (
"reflect"

"k8s.io/apimachinery/pkg/runtime/schema"
"knative.dev/pkg/apis"
)

var (
_ TektonComponentStatus = (*TektonPipelineStatus)(nil)
const (
PreReconciler apis.ConditionType = "PreReconciler"
InstallerSetAvailable apis.ConditionType = "InstallerSetAvailable"
InstallerSetReady apis.ConditionType = "InstallerSetReady"
PostReconciler apis.ConditionType = "PostReconciler"
)

var (
pipelineCondSet = apis.NewLivingConditionSet(
DependenciesInstalled,
DeploymentsAvailable,
InstallSucceeded,
PreReconciler,
InstallerSetAvailable,
InstallerSetReady,
PostReconciler,
)
)

// GroupVersionKind returns SchemeGroupVersion of a TektonPipeline
func (tp *TektonPipeline) GroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind(KindTektonPipeline)
}

// GetCondition returns the current condition of a given condition type
func (tp *TektonPipeline) GetGroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind(KindTektonPipeline)
}

func (t TektonPipelineStatus) IsInitialized() bool {
return !reflect.DeepEqual(t, TektonPipelineStatus{})
}

func (tps *TektonPipelineStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return pipelineCondSet.Manage(tps).GetCondition(t)
}

// InitializeConditions initializes conditions of an TektonPipelineStatus
func (tps *TektonPipelineStatus) InitializeConditions() {
pipelineCondSet.Manage(tps).InitializeConditions()
}

// IsReady looks at the conditions returns true if they are all true.
func (tps *TektonPipelineStatus) IsReady() bool {
return pipelineCondSet.Manage(tps).IsHappy()
}

// MarkInstallSucceeded marks the InstallationSucceeded status as true.
func (tps *TektonPipelineStatus) MarkInstallSucceeded() {
pipelineCondSet.Manage(tps).MarkTrue(InstallSucceeded)
if tps.GetCondition(DependenciesInstalled).IsUnknown() {
// Assume deps are installed if we're not sure
tps.MarkDependenciesInstalled()
}
func (tps *TektonPipelineStatus) MarkPreReconcilerComplete() {
pipelineCondSet.Manage(tps).MarkTrue(PreReconciler)
}

// MarkInstallFailed marks the InstallationSucceeded status as false with the given
// message.
func (tps *TektonPipelineStatus) MarkInstallFailed(msg string) {
pipelineCondSet.Manage(tps).MarkFalse(
InstallSucceeded,
"Error",
"Install failed with message: %s", msg)
func (tps *TektonPipelineStatus) MarkInstallerSetAvailable() {
pipelineCondSet.Manage(tps).MarkTrue(InstallerSetAvailable)
}

// MarkDeploymentsAvailable marks the DeploymentsAvailable status as true.
func (tps *TektonPipelineStatus) MarkDeploymentsAvailable() {
pipelineCondSet.Manage(tps).MarkTrue(DeploymentsAvailable)
func (tps *TektonPipelineStatus) MarkInstallerSetReady() {
pipelineCondSet.Manage(tps).MarkTrue(InstallerSetReady)
}

// MarkDeploymentsNotReady marks the DeploymentsAvailable status as false and calls out
// it's waiting for deployments.
func (tps *TektonPipelineStatus) MarkDeploymentsNotReady() {
func (tps *TektonPipelineStatus) MarkPostReconcilerComplete() {
pipelineCondSet.Manage(tps).MarkTrue(PostReconciler)
}

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

// MarkDependenciesInstalled marks the DependenciesInstalled status as true.
func (tps *TektonPipelineStatus) MarkDependenciesInstalled() {
pipelineCondSet.Manage(tps).MarkTrue(DependenciesInstalled)
func (tps *TektonPipelineStatus) MarkPreReconcilerFailed(msg string) {
tps.MarkNotReady("PreReconciliation failed")
pipelineCondSet.Manage(tps).MarkFalse(
PreReconciler,
"Error",
"PreReconciliation failed with message: %s", msg)
}

// MarkDependencyInstalling marks the DependenciesInstalled status as false with the
// given message.
func (tps *TektonPipelineStatus) MarkDependencyInstalling(msg string) {
func (tps *TektonPipelineStatus) MarkInstallerSetNotAvailable(msg string) {
tps.MarkNotReady("TektonInstallerSet not ready")
pipelineCondSet.Manage(tps).MarkFalse(
DependenciesInstalled,
"Installing",
"Dependency installing: %s", msg)
InstallerSetAvailable,
"Error",
"Installer set not ready: %s", msg)
}

// MarkDependencyMissing marks the DependenciesInstalled status as false with the
// given message.
func (tps *TektonPipelineStatus) MarkDependencyMissing(msg string) {
func (tps *TektonPipelineStatus) MarkInstallerSetNotReady(msg string) {
tps.MarkNotReady("TektonInstallerSet not ready")
pipelineCondSet.Manage(tps).MarkFalse(
InstallerSetReady,
"Error",
"Installer set not ready: %s", msg)
}

func (tps *TektonPipelineStatus) MarkPostReconcilerFailed(msg string) {
tps.MarkNotReady("PostReconciliation failed")
pipelineCondSet.Manage(tps).MarkFalse(
DependenciesInstalled,
PostReconciler,
"Error",
"Dependency missing: %s", msg)
"PostReconciliation failed with message: %s", msg)
}

// TODO: below methods are not required for TektonPipeline
// but as extension implements TektonComponent we need to defined them
// this will be removed

func (tps *TektonPipelineStatus) GetTektonInstallerSet() string {
return tps.TektonInstallerSet
}

func (tps *TektonPipelineStatus) SetTektonInstallerSet(installerSet string) {
tps.TektonInstallerSet = installerSet
}

// GetVersion gets the currently installed version of the component.
func (tps *TektonPipelineStatus) GetVersion() string {
return tps.Version
}

// SetVersion sets the currently installed version of the component.
func (tps *TektonPipelineStatus) SetVersion(version string) {
tps.Version = version
}

// GetManifests gets the url links of the manifests.
func (tps *TektonPipelineStatus) GetManifests() []string {
return tps.Manifests
func (tps *TektonPipelineStatus) MarkInstallSucceeded() {
panic("implement me")
}

func (tps *TektonPipelineStatus) MarkInstallFailed(msg string) {
panic("implement me")
}

func (tps *TektonPipelineStatus) MarkDeploymentsAvailable() {
panic("implement me")
}

// SetVersion sets the url links of the manifests.
func (tps *TektonPipelineStatus) SetManifests(manifests []string) {
tps.Manifests = manifests
func (tps *TektonPipelineStatus) MarkDeploymentsNotReady() {
panic("implement me")
}

func (tps *TektonPipelineStatus) MarkDependenciesInstalled() {
panic("implement me")
}

func (tps *TektonPipelineStatus) MarkDependencyInstalling(msg string) {
panic("implement me")
}

func (tps *TektonPipelineStatus) MarkDependencyMissing(msg string) {
panic("implement me")
}

func (tps *TektonPipelineStatus) GetManifests() []string {
panic("implement me")
}
133 changes: 53 additions & 80 deletions pkg/apis/operator/v1alpha1/tektonpipeline_lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestTektonPipelineGroupVersionKind(t *testing.T) {
Version: SchemaVersion,
Kind: KindTektonPipeline,
}
if got := r.GroupVersionKind(); got != want {
if got := r.GetGroupVersionKind(); got != want {
t.Errorf("got: %v, want: %v", got, want)
}
}
Expand All @@ -39,31 +39,30 @@ func TestTektonPipelineHappyPath(t *testing.T) {
tp := &TektonPipelineStatus{}
tp.InitializeConditions()

apistest.CheckConditionOngoing(tp, DependenciesInstalled, t)
apistest.CheckConditionOngoing(tp, DeploymentsAvailable, t)
apistest.CheckConditionOngoing(tp, InstallSucceeded, t)

// Install succeeds.
tp.MarkInstallSucceeded()
// Dependencies are assumed successful too.
apistest.CheckConditionSucceeded(tp, DependenciesInstalled, t)
apistest.CheckConditionOngoing(tp, DeploymentsAvailable, t)
apistest.CheckConditionSucceeded(tp, InstallSucceeded, t)

// Deployments are not available at first.
tp.MarkDeploymentsNotReady()
apistest.CheckConditionSucceeded(tp, DependenciesInstalled, t)
apistest.CheckConditionFailed(tp, DeploymentsAvailable, t)
apistest.CheckConditionSucceeded(tp, InstallSucceeded, t)
if ready := tp.IsReady(); ready {
t.Errorf("tp.IsReady() = %v, want false", ready)
}
apistest.CheckConditionOngoing(tp, PreReconciler, t)
apistest.CheckConditionOngoing(tp, InstallerSetAvailable, t)
apistest.CheckConditionOngoing(tp, InstallerSetReady, t)
apistest.CheckConditionOngoing(tp, PostReconciler, t)

// Pre reconciler completes execution
tp.MarkPreReconcilerComplete()
apistest.CheckConditionSucceeded(tp, PreReconciler, t)

// Installer set created
tp.MarkInstallerSetAvailable()
apistest.CheckConditionSucceeded(tp, InstallerSetAvailable, t)

// InstallerSet is not ready when deployment pods are not up
tp.MarkInstallerSetNotReady("waiting for deployments")
apistest.CheckConditionFailed(tp, InstallerSetReady, t)

// InstallerSet and then PostReconciler become ready and we're good.
tp.MarkInstallerSetReady()
apistest.CheckConditionSucceeded(tp, InstallerSetReady, t)

tp.MarkPostReconcilerComplete()
apistest.CheckConditionSucceeded(tp, PostReconciler, t)

// Deployments become ready and we're good.
tp.MarkDeploymentsAvailable()
apistest.CheckConditionSucceeded(tp, DependenciesInstalled, t)
apistest.CheckConditionSucceeded(tp, DeploymentsAvailable, t)
apistest.CheckConditionSucceeded(tp, InstallSucceeded, t)
if ready := tp.IsReady(); !ready {
t.Errorf("tp.IsReady() = %v, want true", ready)
}
Expand All @@ -73,66 +72,40 @@ func TestTektonPipelineErrorPath(t *testing.T) {
tp := &TektonPipelineStatus{}
tp.InitializeConditions()

apistest.CheckConditionOngoing(tp, DependenciesInstalled, t)
apistest.CheckConditionOngoing(tp, DeploymentsAvailable, t)
apistest.CheckConditionOngoing(tp, InstallSucceeded, t)

// Install fails.
tp.MarkInstallFailed("test")
apistest.CheckConditionOngoing(tp, DependenciesInstalled, t)
apistest.CheckConditionOngoing(tp, DeploymentsAvailable, t)
apistest.CheckConditionFailed(tp, InstallSucceeded, t)

// Dependencies are installing.
tp.MarkDependencyInstalling("testing")
apistest.CheckConditionFailed(tp, DependenciesInstalled, t)
apistest.CheckConditionOngoing(tp, DeploymentsAvailable, t)
apistest.CheckConditionFailed(tp, InstallSucceeded, t)

// Install now succeeds.
tp.MarkInstallSucceeded()
apistest.CheckConditionFailed(tp, DependenciesInstalled, t)
apistest.CheckConditionOngoing(tp, DeploymentsAvailable, t)
apistest.CheckConditionSucceeded(tp, InstallSucceeded, t)
if ready := tp.IsReady(); ready {
t.Errorf("tp.IsReady() = %v, want false", ready)
}
apistest.CheckConditionOngoing(tp, PreReconciler, t)
apistest.CheckConditionOngoing(tp, InstallerSetAvailable, t)
apistest.CheckConditionOngoing(tp, InstallerSetReady, t)
apistest.CheckConditionOngoing(tp, PostReconciler, t)

// Deployments become ready
tp.MarkDeploymentsAvailable()
apistest.CheckConditionFailed(tp, DependenciesInstalled, t)
apistest.CheckConditionSucceeded(tp, DeploymentsAvailable, t)
apistest.CheckConditionSucceeded(tp, InstallSucceeded, t)
if ready := tp.IsReady(); ready {
t.Errorf("tp.IsReady() = %v, want false", ready)
}
// Pre reconciler completes execution
tp.MarkPreReconcilerComplete()
apistest.CheckConditionSucceeded(tp, PreReconciler, t)

// Installer set created
tp.MarkInstallerSetAvailable()
apistest.CheckConditionSucceeded(tp, InstallerSetAvailable, t)

// InstallerSet is not ready when deployment pods are not up
tp.MarkInstallerSetNotReady("waiting for deployments")
apistest.CheckConditionFailed(tp, InstallerSetReady, t)

// InstallerSet and then PostReconciler become ready and we're good.
tp.MarkInstallerSetReady()
apistest.CheckConditionSucceeded(tp, InstallerSetReady, t)

tp.MarkPostReconcilerComplete()
apistest.CheckConditionSucceeded(tp, PostReconciler, t)

// Finally, dependencies become available.
tp.MarkDependenciesInstalled()
apistest.CheckConditionSucceeded(tp, DependenciesInstalled, t)
apistest.CheckConditionSucceeded(tp, DeploymentsAvailable, t)
apistest.CheckConditionSucceeded(tp, InstallSucceeded, t)
if ready := tp.IsReady(); !ready {
t.Errorf("tp.IsReady() = %v, want true", ready)
}
}

func TestTektonPipelineExternalDependency(t *testing.T) {
tp := &TektonPipelineStatus{}
tp.InitializeConditions()

// External marks dependency as failed.
tp.MarkDependencyMissing("test")

// Install succeeds.
tp.MarkInstallSucceeded()
apistest.CheckConditionFailed(tp, DependenciesInstalled, t)
apistest.CheckConditionOngoing(tp, DeploymentsAvailable, t)
apistest.CheckConditionSucceeded(tp, InstallSucceeded, t)
// In further reconciliation deployment might fail and installer
// set will change to not ready

// Dependencies are now ready.
tp.MarkDependenciesInstalled()
apistest.CheckConditionSucceeded(tp, DependenciesInstalled, t)
apistest.CheckConditionOngoing(tp, DeploymentsAvailable, t)
apistest.CheckConditionSucceeded(tp, InstallSucceeded, t)
tp.MarkInstallerSetNotReady("webhook not ready")
apistest.CheckConditionFailed(tp, InstallerSetReady, t)
if ready := tp.IsReady(); ready {
t.Errorf("tp.IsReady() = %v, want false", ready)
}
}
Loading

0 comments on commit 811229d

Please sign in to comment.