Skip to content

Commit

Permalink
moduletemplate_types: Add Custom State Check
Browse files Browse the repository at this point in the history
  • Loading branch information
LeelaChacha committed May 27, 2023
1 parent 850757f commit 7dc0904
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 19 deletions.
2 changes: 2 additions & 0 deletions api/v1beta1/moduletemplate_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ func (src *ModuleTemplate) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Channel = src.Spec.Channel
dst.Spec.Data = src.Spec.Data
dst.Spec.Descriptor = src.Spec.Descriptor
dst.Spec.CustomStateCheck = (*v1beta2.CustomStateCheck)(src.Spec.CustomStateCheck)
return nil
}

Expand All @@ -21,6 +22,7 @@ func (dst *ModuleTemplate) ConvertFrom(srcRaw conversion.Hub) error {
dst.Spec.Channel = src.Spec.Channel
dst.Spec.Data = src.Spec.Data
dst.Spec.Descriptor = src.Spec.Descriptor
dst.Spec.CustomStateCheck = (*CustomStateCheck)(src.Spec.CustomStateCheck)
dst.Spec.Target = TargetRemote
return nil
}
3 changes: 3 additions & 0 deletions api/v1beta2/kyma_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ type ModuleStatus struct {

// Resource contains information about the created module CR.
Resource *TrackingObject `json:"resource,omitempty"`

// CustomStateCheck for advanced Module State determination
CustomStateCheck *CustomStateCheck `json:"customStateCheck,omitempty"`
}

// TrackingObject contains metav1.TypeMeta and PartialMeta to allow a generation based object tracking.
Expand Down
11 changes: 11 additions & 0 deletions api/v1beta2/moduletemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@ type ModuleTemplateSpec struct {
//
//+kubebuilder:pruning:PreserveUnknownFields
Descriptor runtime.RawExtension `json:"descriptor"`

// CustomStateCheck for advanced Module State determination
CustomStateCheck *CustomStateCheck `json:"customStateCheck,omitempty"`
}

type CustomStateCheck struct {
// JsonPath specifies the JSON path to the state variable in the Module CR
JsonPath string `json:"jsonPath"`

// Value is the value at the JsonPath for which the Module CR state is set to "Ready" in Kyma CR
Value string `json:"value"`
}

func (spec *ModuleTemplateSpec) GetDescriptor(opts ...compdesc.DecodeOption) (*Descriptor, error) {
Expand Down
30 changes: 30 additions & 0 deletions config/crd/bases/operator.kyma-project.io_kymas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,21 @@ spec:
lookup to be necessary that maybe picks a different ModuleTemplate,
which is why we need to reconcile.
type: string
customStateCheck:
description: CustomStateCheck for advanced Module State determination
properties:
jsonPath:
description: JsonPath specifies the JSON path to the state
variable in the Module CR
type: string
value:
description: Value is the value at the JsonPath for which
the Module CR state is set to "Ready" in Kyma CR
type: string
required:
- jsonPath
- value
type: object
fqdn:
description: FQDN is the fully qualified domain name of the
module. In the ModuleTemplate it is located in .spec.descriptor.component.name
Expand Down Expand Up @@ -612,6 +627,21 @@ spec:
lookup to be necessary that maybe picks a different ModuleTemplate,
which is why we need to reconcile.
type: string
customStateCheck:
description: CustomStateCheck for advanced Module State determination
properties:
jsonPath:
description: JsonPath specifies the JSON path to the state
variable in the Module CR
type: string
value:
description: Value is the value at the JsonPath for which
the Module CR state is set to "Ready" in Kyma CR
type: string
required:
- jsonPath
- value
type: object
fqdn:
description: FQDN is the fully qualified domain name of the
module. In the ModuleTemplate it is located in .spec.descriptor.component.name
Expand Down
30 changes: 30 additions & 0 deletions config/crd/bases/operator.kyma-project.io_moduletemplates.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ spec:
minLength: 3
pattern: ^[a-z]+$
type: string
customStateCheck:
description: CustomStateCheck for advanced Module State determination
properties:
jsonPath:
description: JsonPath specifies the JSON path to the state variable
in the Module CR
type: string
value:
description: Value is the value at the JsonPath for which the
Module CR state is set to "Ready" in Kyma CR
type: string
required:
- jsonPath
- value
type: object
data:
description: Data is the default set of attributes that are used to
generate the Module. It contains a default set of values for a given
Expand Down Expand Up @@ -124,6 +139,21 @@ spec:
minLength: 3
pattern: ^[a-z]+$
type: string
customStateCheck:
description: CustomStateCheck for advanced Module State determination
properties:
jsonPath:
description: JsonPath specifies the JSON path to the state variable
in the Module CR
type: string
value:
description: Value is the value at the JsonPath for which the
Module CR state is set to "Ready" in Kyma CR
type: string
required:
- jsonPath
- value
type: object
data:
description: Data is the default set of attributes that are used to
generate the Module. It contains a default set of values for a given
Expand Down
2 changes: 1 addition & 1 deletion controllers/moduletemplate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ var _ = Describe("Custom State Check can be used", Ordered, func() {

It("Should create manifest", func() {
Eventually(ManifestExists, Timeout, Interval).
WithArguments(kyma, module).
WithArguments(ctx, kyma, module, controlPlaneClient).
Should(Succeed())
})

Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (
github.com/open-component-model/ocm v0.2.0
github.com/prometheus/client_golang v1.14.0
github.com/stretchr/testify v1.8.2
github.com/tidwall/gjson v1.14.4
github.com/xeipuuv/gojsonschema v1.2.0
go.uber.org/zap v1.24.0
golang.org/x/sync v0.1.0
Expand Down Expand Up @@ -178,6 +179,8 @@ require (
github.com/spf13/cobra v1.6.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/theupdateframework/notary v0.7.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tonglil/buflogr v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,12 @@ github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ
github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tonglil/buflogr v1.0.1 h1:WXFZLKxLfqcVSmckwiMCF8jJwjIgmStJmg63YKRF1p0=
Expand Down
67 changes: 49 additions & 18 deletions pkg/module/sync/runner_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package sync

import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"time"

"github.com/kyma-project/lifecycle-manager/api/v1beta2"
"github.com/kyma-project/lifecycle-manager/pkg/channel"
"github.com/tidwall/gjson"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -161,23 +164,28 @@ func generateModuleStatus(module *common.Module, existStatus *v1beta2.ModuleStat
return *newModuleStatus
}
if module.Template.Err != nil {
return v1beta2.ModuleStatus{
status := v1beta2.ModuleStatus{
Name: module.ModuleName,
Channel: module.Template.DesiredChannel,
FQDN: module.FQDN,
State: v1beta2.StateError,
Message: module.Template.Err.Error(),
}
if module.Template.ModuleTemplate != nil {
status.CustomStateCheck = module.Template.Spec.CustomStateCheck
}
return status
}
manifestObject, ok := module.Object.(*v1beta2.Manifest)
if !ok {
// TODO: impossible case, remove casting check after module use typed Manifest instead of client.Object
return v1beta2.ModuleStatus{
Name: module.ModuleName,
Channel: module.Template.DesiredChannel,
FQDN: module.FQDN,
State: v1beta2.StateError,
Message: ErrManifestConversion.Error(),
Name: module.ModuleName,
Channel: module.Template.DesiredChannel,
FQDN: module.FQDN,
State: v1beta2.StateError,
Message: ErrManifestConversion.Error(),
CustomStateCheck: module.Template.Spec.CustomStateCheck,
}
}
manifestAPIVersion, manifestKind := manifestObject.GetObjectKind().GroupVersionKind().ToAPIVersionAndKind()
Expand All @@ -195,7 +203,7 @@ func generateModuleStatus(module *common.Module, existStatus *v1beta2.ModuleStat
return v1beta2.ModuleStatus{
Name: module.ModuleName,
FQDN: module.FQDN,
State: v1beta2.State(manifestObject.Status.State),
State: stateFromManifest(module.Object, module.Template.Spec.CustomStateCheck),
Channel: module.Template.Spec.Channel,
Version: module.Version,
Manifest: &v1beta2.TrackingObject{
Expand All @@ -206,19 +214,42 @@ func generateModuleStatus(module *common.Module, existStatus *v1beta2.ModuleStat
PartialMeta: v1beta2.PartialMetaFromObject(module.Template),
TypeMeta: metav1.TypeMeta{Kind: templateKind, APIVersion: templateAPIVersion},
},
Resource: moduleResource,
Resource: moduleResource,
CustomStateCheck: module.Template.Spec.CustomStateCheck,
}
}

func stateFromManifest(obj client.Object, customStateCheck *v1beta2.CustomStateCheck) v1beta2.State {
if customStateCheck == nil {
switch manifest := obj.(type) {
case *v1beta2.Manifest:
return v1beta2.State(manifest.Status.State)
case *unstructured.Unstructured:
state, _, _ := unstructured.NestedString(manifest.Object, "status", "state")
return v1beta2.State(state)
default:
return ""
}
} else {
return processCustomStateCheck(obj, customStateCheck)
}
}

func stateFromManifest(obj client.Object) v1beta2.State {
switch manifest := obj.(type) {
case *v1beta2.Manifest:
return v1beta2.State(manifest.Status.State)
case *unstructured.Unstructured:
state, _, _ := unstructured.NestedString(manifest.Object, "status", "state")
return v1beta2.State(state)
default:
return ""
func processCustomStateCheck(obj client.Object, customStateCheck *v1beta2.CustomStateCheck) v1beta2.State {
marshalledObj, err := json.Marshal(obj)
if err != nil {
return v1beta2.StateError
}

customStateCheck.JsonPath, _ = strings.CutPrefix(customStateCheck.JsonPath, ".")
result := gjson.Get(string(marshalledObj), customStateCheck.JsonPath)

if valueFromManifest, ok := result.Value().(string); ok && customStateCheck.Value == valueFromManifest {
return v1beta2.StateReady
} else if !result.Exists() {
return v1beta2.StateError
} else {
return v1beta2.StateProcessing
}
}

Expand All @@ -243,7 +274,7 @@ func DeleteNoLongerExistingModuleStatus(
if apiErrors.IsNotFound(err) {
delete(moduleStatusMap, moduleStatus.Name)
} else {
moduleStatus.State = stateFromManifest(module)
moduleStatus.State = stateFromManifest(module, moduleStatus.CustomStateCheck)
}
}
kyma.Status.Modules = convertToNewModuleStatus(moduleStatusMap)
Expand Down

0 comments on commit 7dc0904

Please sign in to comment.