github.com/kubevela/workflow@v0.6.0/pkg/providers/workspace/workspace.go (about)

     1  /*
     2  Copyright 2022 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package workspace
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"time"
    23  
    24  	monitorContext "github.com/kubevela/pkg/monitor/context"
    25  	"sigs.k8s.io/kind/pkg/errors"
    26  
    27  	"github.com/kubevela/workflow/api/v1alpha1"
    28  	wfContext "github.com/kubevela/workflow/pkg/context"
    29  	"github.com/kubevela/workflow/pkg/cue/model"
    30  	"github.com/kubevela/workflow/pkg/cue/model/value"
    31  	"github.com/kubevela/workflow/pkg/cue/process"
    32  	"github.com/kubevela/workflow/pkg/types"
    33  )
    34  
    35  const (
    36  	// ProviderName is provider name.
    37  	ProviderName = "builtin"
    38  	// ResumeTimeStamp is resume time stamp.
    39  	ResumeTimeStamp = "resumeTimeStamp"
    40  	// SuspendTimeStamp is suspend time stamp.
    41  	SuspendTimeStamp = "suspendTimeStamp"
    42  )
    43  
    44  type provider struct {
    45  	pCtx process.Context
    46  }
    47  
    48  // DoVar get & put variable from context.
    49  func (h *provider) DoVar(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act types.Action) error {
    50  	methodV, err := v.Field("method")
    51  	if err != nil {
    52  		return err
    53  	}
    54  	method, err := methodV.String()
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	pathV, err := v.Field("path")
    60  	if err != nil {
    61  		return err
    62  	}
    63  	path, err := pathV.String()
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	switch method {
    69  	case "Get":
    70  		value, err := wfCtx.GetVar(strings.Split(path, ".")...)
    71  		if err != nil {
    72  			return err
    73  		}
    74  		raw, err := value.String()
    75  		if err != nil {
    76  			return err
    77  		}
    78  		return v.FillRaw(raw, "value")
    79  	case "Put":
    80  		value, err := v.LookupValue("value")
    81  		if err != nil {
    82  			return err
    83  		}
    84  		return wfCtx.SetVar(value, strings.Split(path, ".")...)
    85  	}
    86  	return nil
    87  }
    88  
    89  // Wait let workflow wait.
    90  func (h *provider) Wait(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act types.Action) error {
    91  	cv := v.CueValue()
    92  	if cv.Exists() {
    93  		ret := cv.LookupPath(value.FieldPath("continue"))
    94  		if ret.Exists() {
    95  			isContinue, err := ret.Bool()
    96  			if err == nil && isContinue {
    97  				return nil
    98  			}
    99  		}
   100  	}
   101  	msg, _ := v.GetString("message")
   102  	act.Wait(msg)
   103  	return nil
   104  }
   105  
   106  // Break let workflow terminate.
   107  func (h *provider) Break(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act types.Action) error {
   108  	var msg string
   109  	if v != nil {
   110  		msg, _ = v.GetString("message")
   111  	}
   112  	act.Terminate(msg)
   113  	return nil
   114  }
   115  
   116  // Fail let the step fail, its status is failed and reason is Action
   117  func (h *provider) Fail(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act types.Action) error {
   118  	var msg string
   119  	if v != nil {
   120  		msg, _ = v.GetString("message")
   121  	}
   122  	act.Fail(msg)
   123  	return nil
   124  }
   125  
   126  // Suspend let the step suspend, its status is suspending and reason is Suspend
   127  func (h *provider) Suspend(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act types.Action) error {
   128  	stepID := fmt.Sprint(h.pCtx.GetData(model.ContextStepSessionID))
   129  	timestamp := wfCtx.GetMutableValue(stepID, ResumeTimeStamp)
   130  	var msg string
   131  	if v != nil {
   132  		msg, _ = v.GetString("message")
   133  		if msg == "" {
   134  			msg = fmt.Sprintf("Suspended by field %s", v.FieldName())
   135  		}
   136  	}
   137  	if timestamp != "" {
   138  		t, err := time.Parse(time.RFC3339, timestamp)
   139  		if err != nil {
   140  			return errors.Wrap(err, "failed to parse timestamp")
   141  		}
   142  		if time.Now().After(t) {
   143  			act.Resume("")
   144  			return nil
   145  		}
   146  		act.Suspend(msg)
   147  		return nil
   148  	}
   149  	if v != nil {
   150  		d, _ := v.LookupValue("duration")
   151  		if d != nil && d.CueValue().Exists() {
   152  			dur, err := d.CueValue().String()
   153  			if err != nil {
   154  				return err
   155  			}
   156  			duration, err := time.ParseDuration(dur)
   157  			if err != nil {
   158  				return errors.Wrap(err, "failed to parse duration")
   159  			}
   160  			wfCtx.SetMutableValue(time.Now().Add(duration).Format(time.RFC3339), stepID, ResumeTimeStamp)
   161  		}
   162  	}
   163  	if ts := wfCtx.GetMutableValue(stepID, v.FieldName(), SuspendTimeStamp); ts != "" {
   164  		if act.GetStatus().Phase == v1alpha1.WorkflowStepPhaseRunning {
   165  			// if it is already suspended before and has been resumed, we should not suspend it again.
   166  			return nil
   167  		}
   168  	} else {
   169  		wfCtx.SetMutableValue(time.Now().Format(time.RFC3339), stepID, v.FieldName(), SuspendTimeStamp)
   170  	}
   171  	act.Suspend(msg)
   172  	return nil
   173  }
   174  
   175  // Message writes message to step status, note that the message will be overwritten by the next message.
   176  func (h *provider) Message(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act types.Action) error {
   177  	var msg string
   178  	if v != nil {
   179  		msg, _ = v.GetString("message")
   180  	}
   181  	act.Message(msg)
   182  	return nil
   183  }
   184  
   185  // Install register handler to provider discover.
   186  func Install(p types.Providers, pCtx process.Context) {
   187  	prd := &provider{
   188  		pCtx: pCtx,
   189  	}
   190  	p.Register(ProviderName, map[string]types.Handler{
   191  		"wait":    prd.Wait,
   192  		"break":   prd.Break,
   193  		"fail":    prd.Fail,
   194  		"message": prd.Message,
   195  		"var":     prd.DoVar,
   196  		"suspend": prd.Suspend,
   197  	})
   198  }