mirror of
https://github.com/usual2970/certimate.git
synced 2025-07-29 13:44:29 +00:00
refactor: workflow monitor(aka inspect) node
This commit is contained in:
@@ -34,7 +34,7 @@ func NewApplyNode(node *domain.WorkflowNode) *applyNode {
|
||||
}
|
||||
|
||||
func (n *applyNode) Process(ctx context.Context) error {
|
||||
n.logger.Info("ready to apply ...")
|
||||
n.logger.Info("ready to obtain certificiate ...")
|
||||
|
||||
// 查询上次执行结果
|
||||
lastOutput, err := n.outputRepo.GetByNodeId(ctx, n.node.Id)
|
||||
@@ -63,7 +63,7 @@ func (n *applyNode) Process(ctx context.Context) error {
|
||||
// 申请证书
|
||||
applyResult, err := applicant.Apply(ctx)
|
||||
if err != nil {
|
||||
n.logger.Warn("failed to apply")
|
||||
n.logger.Warn("failed to obtain certificiate")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ func (n *applyNode) Process(ctx context.Context) error {
|
||||
n.outputs[outputCertificateValidatedKey] = "true"
|
||||
n.outputs[outputCertificateDaysLeftKey] = fmt.Sprintf("%d", int(time.Until(certificate.ExpireAt).Hours()/24))
|
||||
|
||||
n.logger.Info("apply completed")
|
||||
n.logger.Info("application completed")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ func (n *applyNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workflo
|
||||
if expirationTime > renewalInterval {
|
||||
n.outputs[outputCertificateValidatedKey] = "true"
|
||||
n.outputs[outputCertificateDaysLeftKey] = fmt.Sprintf("%d", int(expirationTime.Hours()/24))
|
||||
return true, fmt.Sprintf("the certificate has already been issued (expires in %dd, next renewal in %dd)", int(expirationTime.Hours()/24), currentNodeConfig.SkipBeforeExpiryDays)
|
||||
return true, fmt.Sprintf("the certificate has already been issued (expires in %d day(s), next renewal in %d day(s))", int(expirationTime.Hours()/24), currentNodeConfig.SkipBeforeExpiryDays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ func NewDeployNode(node *domain.WorkflowNode) *deployNode {
|
||||
}
|
||||
|
||||
func (n *deployNode) Process(ctx context.Context) error {
|
||||
n.logger.Info("ready to deploy ...")
|
||||
n.logger.Info("ready to deploy certificate ...")
|
||||
|
||||
// 查询上次执行结果
|
||||
lastOutput, err := n.outputRepo.GetByNodeId(ctx, n.node.Id)
|
||||
@@ -78,7 +78,7 @@ func (n *deployNode) Process(ctx context.Context) error {
|
||||
|
||||
// 部署证书
|
||||
if err := deployer.Deploy(ctx); err != nil {
|
||||
n.logger.Warn("failed to deploy")
|
||||
n.logger.Warn("failed to deploy certificate")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -95,8 +95,7 @@ func (n *deployNode) Process(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
n.logger.Info("deploy completed")
|
||||
|
||||
n.logger.Info("deployment completed")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -1,191 +0,0 @@
|
||||
package nodeprocessor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
)
|
||||
|
||||
type inspectNode struct {
|
||||
node *domain.WorkflowNode
|
||||
*nodeProcessor
|
||||
*nodeOutputer
|
||||
}
|
||||
|
||||
func NewInspectNode(node *domain.WorkflowNode) *inspectNode {
|
||||
return &inspectNode{
|
||||
node: node,
|
||||
nodeProcessor: newNodeProcessor(node),
|
||||
nodeOutputer: newNodeOutputer(),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *inspectNode) Process(ctx context.Context) error {
|
||||
n.logger.Info("entering inspect certificate node...")
|
||||
|
||||
nodeConfig := n.node.GetConfigForInspect()
|
||||
|
||||
err := n.inspect(ctx, nodeConfig)
|
||||
if err != nil {
|
||||
n.logger.Warn("inspect certificate failed: " + err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *inspectNode) inspect(ctx context.Context, nodeConfig domain.WorkflowNodeConfigForInspect) error {
|
||||
maxRetries := 3
|
||||
retryInterval := 2 * time.Second
|
||||
|
||||
var lastError error
|
||||
var certInfo *x509.Certificate
|
||||
|
||||
host := nodeConfig.Host
|
||||
|
||||
port := nodeConfig.Port
|
||||
if port == "" {
|
||||
port = "443"
|
||||
}
|
||||
|
||||
domain := nodeConfig.Domain
|
||||
if domain == "" {
|
||||
domain = host
|
||||
}
|
||||
|
||||
path := nodeConfig.Path
|
||||
if path != "" && !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
|
||||
targetAddr := fmt.Sprintf("%s:%s", host, port)
|
||||
n.logger.Info(fmt.Sprintf("Inspecting certificate at %s (validating domain: %s)", targetAddr, domain))
|
||||
|
||||
for attempt := 0; attempt < maxRetries; attempt++ {
|
||||
if attempt > 0 {
|
||||
n.logger.Info(fmt.Sprintf("Retry #%d connecting to %s", attempt, targetAddr))
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(retryInterval):
|
||||
// Wait for retry interval
|
||||
}
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 10 * time.Second,
|
||||
}).DialContext,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: domain, // Set SNI to domain for proper certificate selection
|
||||
},
|
||||
ForceAttemptHTTP2: false,
|
||||
DisableKeepAlives: true,
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: 15 * time.Second,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
scheme := "https"
|
||||
urlStr := fmt.Sprintf("%s://%s", scheme, targetAddr)
|
||||
if path != "" {
|
||||
urlStr = urlStr + path
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "HEAD", urlStr, nil)
|
||||
if err != nil {
|
||||
lastError = fmt.Errorf("failed to create HTTP request: %w", err)
|
||||
n.logger.Warn(fmt.Sprintf("Request creation attempt #%d failed: %s", attempt+1, lastError.Error()))
|
||||
continue
|
||||
}
|
||||
|
||||
if domain != host {
|
||||
req.Host = domain
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "CertificateValidator/1.0")
|
||||
req.Header.Set("Accept", "*/*")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
lastError = fmt.Errorf("HTTP request failed: %w", err)
|
||||
n.logger.Warn(fmt.Sprintf("Connection attempt #%d failed: %s", attempt+1, lastError.Error()))
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.TLS == nil || len(resp.TLS.PeerCertificates) == 0 {
|
||||
resp.Body.Close()
|
||||
lastError = fmt.Errorf("no TLS certificates received in HTTP response")
|
||||
n.logger.Warn(fmt.Sprintf("Certificate retrieval attempt #%d failed: %s", attempt+1, lastError.Error()))
|
||||
continue
|
||||
}
|
||||
|
||||
certInfo = resp.TLS.PeerCertificates[0]
|
||||
resp.Body.Close()
|
||||
|
||||
lastError = nil
|
||||
n.logger.Info(fmt.Sprintf("Successfully retrieved certificate from %s", targetAddr))
|
||||
break
|
||||
}
|
||||
|
||||
if lastError != nil {
|
||||
return fmt.Errorf("failed to retrieve certificate after %d attempts: %w", maxRetries, lastError)
|
||||
}
|
||||
|
||||
if certInfo == nil {
|
||||
outputs := map[string]any{
|
||||
outputCertificateValidatedKey: "false",
|
||||
outputCertificateDaysLeftKey: "0",
|
||||
}
|
||||
n.setOutputs(outputs)
|
||||
return nil
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
isValidTime := now.Before(certInfo.NotAfter) && now.After(certInfo.NotBefore)
|
||||
|
||||
domainMatch := true
|
||||
if err := certInfo.VerifyHostname(domain); err != nil {
|
||||
domainMatch = false
|
||||
}
|
||||
|
||||
isValid := isValidTime && domainMatch
|
||||
|
||||
daysRemaining := math.Floor(certInfo.NotAfter.Sub(now).Hours() / 24)
|
||||
|
||||
isValidStr := "false"
|
||||
if isValid {
|
||||
isValidStr = "true"
|
||||
}
|
||||
|
||||
outputs := map[string]any{
|
||||
outputCertificateValidatedKey: isValidStr,
|
||||
outputCertificateDaysLeftKey: fmt.Sprintf("%d", int(daysRemaining)),
|
||||
}
|
||||
|
||||
n.setOutputs(outputs)
|
||||
|
||||
n.logger.Info(fmt.Sprintf("Certificate inspection completed - Target: %s, Domain: %s, Valid: %s, Days Remaining: %d",
|
||||
targetAddr, domain, isValidStr, int(daysRemaining)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *inspectNode) setOutputs(outputs map[string]any) {
|
||||
n.outputs = outputs
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
package nodeprocessor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
)
|
||||
|
||||
func Test_inspectWebsiteCertificateNode_inspect(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
nodeConfig domain.WorkflowNodeConfigForInspect
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "test1",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
nodeConfig: domain.WorkflowNodeConfigForInspect{
|
||||
Domain: "baidu.com",
|
||||
Port: "443",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
n := NewInspectNode(&domain.WorkflowNode{})
|
||||
if err := n.inspect(tt.args.ctx, tt.args.nodeConfig); (err != nil) != tt.wantErr {
|
||||
t.Errorf("inspectWebsiteCertificateNode.inspect() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
164
internal/workflow/node-processor/monitor_node.go
Normal file
164
internal/workflow/node-processor/monitor_node.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package nodeprocessor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
)
|
||||
|
||||
type monitorNode struct {
|
||||
node *domain.WorkflowNode
|
||||
*nodeProcessor
|
||||
*nodeOutputer
|
||||
}
|
||||
|
||||
func NewMonitorNode(node *domain.WorkflowNode) *monitorNode {
|
||||
return &monitorNode{
|
||||
node: node,
|
||||
nodeProcessor: newNodeProcessor(node),
|
||||
nodeOutputer: newNodeOutputer(),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *monitorNode) Process(ctx context.Context) error {
|
||||
n.logger.Info("ready to monitor certificate ...")
|
||||
|
||||
nodeConfig := n.node.GetConfigForMonitor()
|
||||
|
||||
targetAddr := fmt.Sprintf("%s:%d", nodeConfig.Host, nodeConfig.Port)
|
||||
if nodeConfig.Port == 0 {
|
||||
targetAddr = fmt.Sprintf("%s:443", nodeConfig.Host)
|
||||
}
|
||||
|
||||
targetDomain := nodeConfig.Domain
|
||||
if targetDomain == "" {
|
||||
targetDomain = nodeConfig.Host
|
||||
}
|
||||
|
||||
n.logger.Info(fmt.Sprintf("retrieving certificate at %s (domain: %s)", targetAddr, targetDomain))
|
||||
|
||||
const MAX_ATTEMPTS = 3
|
||||
const RETRY_INTERVAL = 2 * time.Second
|
||||
var cert *x509.Certificate
|
||||
var err error
|
||||
for attempt := 0; attempt < MAX_ATTEMPTS; attempt++ {
|
||||
if attempt > 0 {
|
||||
n.logger.Info(fmt.Sprintf("retry %d time(s) ...", attempt, targetAddr))
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(RETRY_INTERVAL):
|
||||
}
|
||||
}
|
||||
|
||||
cert, err = n.tryRetrieveCert(ctx, targetAddr, targetDomain, nodeConfig.RequestPath)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
n.logger.Warn("failed to monitor certificate")
|
||||
return err
|
||||
} else {
|
||||
if cert == nil {
|
||||
n.logger.Warn("no ssl certificates retrieved in http response")
|
||||
|
||||
outputs := map[string]any{
|
||||
outputCertificateValidatedKey: strconv.FormatBool(false),
|
||||
outputCertificateDaysLeftKey: strconv.FormatInt(0, 10),
|
||||
}
|
||||
n.setOutputs(outputs)
|
||||
} else {
|
||||
n.logger.Info(fmt.Sprintf("ssl certificate retrieved (serial='%s', subject='%s', issuer='%s', not_before='%s', not_after='%s', sans='%s')",
|
||||
cert.SerialNumber, cert.Subject.String(), cert.Issuer.String(),
|
||||
cert.NotBefore.Format(time.RFC3339), cert.NotAfter.Format(time.RFC3339),
|
||||
strings.Join(cert.DNSNames, ";")),
|
||||
)
|
||||
|
||||
now := time.Now()
|
||||
isCertPeriodValid := now.Before(cert.NotAfter) && now.After(cert.NotBefore)
|
||||
isCertHostMatched := true
|
||||
if err := cert.VerifyHostname(targetDomain); err != nil {
|
||||
isCertHostMatched = false
|
||||
}
|
||||
|
||||
validated := isCertPeriodValid && isCertHostMatched
|
||||
daysLeft := int(math.Floor(cert.NotAfter.Sub(now).Hours() / 24))
|
||||
outputs := map[string]any{
|
||||
outputCertificateValidatedKey: strconv.FormatBool(validated),
|
||||
outputCertificateDaysLeftKey: strconv.FormatInt(int64(daysLeft), 10),
|
||||
}
|
||||
n.setOutputs(outputs)
|
||||
|
||||
if validated {
|
||||
n.logger.Info(fmt.Sprintf("the certificate is valid, and will expire in %d day(s)", daysLeft))
|
||||
} else {
|
||||
n.logger.Warn(fmt.Sprintf("the certificate is invalid", validated))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n.logger.Info("monitoring completed")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *monitorNode) tryRetrieveCert(ctx context.Context, addr, domain, requestPath string) (_cert *x509.Certificate, _err error) {
|
||||
transport := &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 10 * time.Second,
|
||||
}).DialContext,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
ForceAttemptHTTP2: false,
|
||||
DisableKeepAlives: true,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: 15 * time.Second,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("https://%s/%s", addr, strings.TrimLeft(requestPath, "/"))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodHead, url, nil)
|
||||
if err != nil {
|
||||
_err = fmt.Errorf("failed to create http request: %w", err)
|
||||
n.logger.Warn(fmt.Sprintf("failed to create http request: %w", err))
|
||||
return nil, _err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "certimate")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
_err = fmt.Errorf("failed to send http request: %w", err)
|
||||
n.logger.Warn(fmt.Sprintf("failed to send http request: %w", err))
|
||||
return nil, _err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.TLS == nil || len(resp.TLS.PeerCertificates) == 0 {
|
||||
return nil, _err
|
||||
}
|
||||
|
||||
_cert = resp.TLS.PeerCertificates[0]
|
||||
return _cert, nil
|
||||
}
|
||||
|
||||
func (n *monitorNode) setOutputs(outputs map[string]any) {
|
||||
n.outputs = outputs
|
||||
}
|
28
internal/workflow/node-processor/monitor_node_test.go
Normal file
28
internal/workflow/node-processor/monitor_node_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package nodeprocessor_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"testing"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
nodeprocessor "github.com/usual2970/certimate/internal/workflow/node-processor"
|
||||
)
|
||||
|
||||
func Test_MonitorNode(t *testing.T) {
|
||||
t.Run("Monitor", func(t *testing.T) {
|
||||
node := nodeprocessor.NewMonitorNode(&domain.WorkflowNode{
|
||||
Id: "test",
|
||||
Type: domain.WorkflowNodeTypeMonitor,
|
||||
Name: "test",
|
||||
Config: map[string]any{
|
||||
"host": "baidu.com",
|
||||
"port": 443,
|
||||
},
|
||||
})
|
||||
node.SetLogger(slog.Default())
|
||||
if err := node.Process(context.Background()); err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
}
|
||||
})
|
||||
}
|
@@ -28,7 +28,7 @@ func NewNotifyNode(node *domain.WorkflowNode) *notifyNode {
|
||||
}
|
||||
|
||||
func (n *notifyNode) Process(ctx context.Context) error {
|
||||
n.logger.Info("ready to notify ...")
|
||||
n.logger.Info("ready to send notification ...")
|
||||
|
||||
nodeConfig := n.node.GetConfigForNotify()
|
||||
|
||||
@@ -51,11 +51,11 @@ func (n *notifyNode) Process(ctx context.Context) error {
|
||||
|
||||
// 发送通知
|
||||
if err := notify.SendToChannel(nodeConfig.Subject, nodeConfig.Message, nodeConfig.Channel, channelConfig); err != nil {
|
||||
n.logger.Warn("failed to notify", slog.String("channel", nodeConfig.Channel))
|
||||
n.logger.Warn("failed to send notification", slog.String("channel", nodeConfig.Channel))
|
||||
return err
|
||||
}
|
||||
|
||||
n.logger.Info("notify completed")
|
||||
n.logger.Info("notification completed")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -73,9 +73,10 @@ func (n *notifyNode) Process(ctx context.Context) error {
|
||||
|
||||
// 推送通知
|
||||
if err := deployer.Notify(ctx); err != nil {
|
||||
n.logger.Warn("failed to notify")
|
||||
n.logger.Warn("failed to send notification")
|
||||
return err
|
||||
}
|
||||
|
||||
n.logger.Info("notification completed")
|
||||
return nil
|
||||
}
|
||||
|
@@ -74,25 +74,25 @@ func GetProcessor(node *domain.WorkflowNode) (NodeProcessor, error) {
|
||||
switch node.Type {
|
||||
case domain.WorkflowNodeTypeStart:
|
||||
return NewStartNode(node), nil
|
||||
case domain.WorkflowNodeTypeCondition:
|
||||
return NewConditionNode(node), nil
|
||||
case domain.WorkflowNodeTypeApply:
|
||||
return NewApplyNode(node), nil
|
||||
case domain.WorkflowNodeTypeUpload:
|
||||
return NewUploadNode(node), nil
|
||||
case domain.WorkflowNodeTypeMonitor:
|
||||
return NewMonitorNode(node), nil
|
||||
case domain.WorkflowNodeTypeDeploy:
|
||||
return NewDeployNode(node), nil
|
||||
case domain.WorkflowNodeTypeNotify:
|
||||
return NewNotifyNode(node), nil
|
||||
case domain.WorkflowNodeTypeCondition:
|
||||
return NewConditionNode(node), nil
|
||||
case domain.WorkflowNodeTypeExecuteSuccess:
|
||||
return NewExecuteSuccessNode(node), nil
|
||||
case domain.WorkflowNodeTypeExecuteFailure:
|
||||
return NewExecuteFailureNode(node), nil
|
||||
case domain.WorkflowNodeTypeInspect:
|
||||
return NewInspectNode(node), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("supported node type: %s", string(node.Type))
|
||||
return nil, fmt.Errorf("unsupported node type: %s", string(node.Type))
|
||||
}
|
||||
|
||||
func getContextWorkflowId(ctx context.Context) string {
|
||||
|
@@ -31,7 +31,7 @@ func NewUploadNode(node *domain.WorkflowNode) *uploadNode {
|
||||
}
|
||||
|
||||
func (n *uploadNode) Process(ctx context.Context) error {
|
||||
n.logger.Info("ready to upload ...")
|
||||
n.logger.Info("ready to upload certiticate ...")
|
||||
|
||||
nodeConfig := n.node.GetConfigForUpload()
|
||||
|
||||
@@ -43,7 +43,7 @@ func (n *uploadNode) Process(ctx context.Context) error {
|
||||
|
||||
// 检测是否可以跳过本次执行
|
||||
if skippable, reason := n.checkCanSkip(ctx, lastOutput); skippable {
|
||||
n.logger.Info(fmt.Sprintf("skip this upload, because %s", reason))
|
||||
n.logger.Info(fmt.Sprintf("skip this uploading, because %s", reason))
|
||||
return nil
|
||||
} else if reason != "" {
|
||||
n.logger.Info(fmt.Sprintf("re-upload, because %s", reason))
|
||||
@@ -72,7 +72,7 @@ func (n *uploadNode) Process(ctx context.Context) error {
|
||||
n.outputs[outputCertificateValidatedKey] = "true"
|
||||
n.outputs[outputCertificateDaysLeftKey] = fmt.Sprintf("%d", int(time.Until(certificate.ExpireAt).Hours()/24))
|
||||
|
||||
n.logger.Info("upload completed")
|
||||
n.logger.Info("uploading completed")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user