Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
67e711d
add cert provider for cert management
yanjiaxin534 Sep 18, 2025
ff5b3bf
add get certificate retry logic
yanjiaxin534 Sep 19, 2025
f5d79a4
fix json formate
yanjiaxin534 Sep 19, 2025
f463629
fix param
yanjiaxin534 Sep 19, 2025
cc10d56
fix \
yanjiaxin534 Sep 19, 2025
6cce19e
remove certmanager
yanjiaxin534 Sep 19, 2025
e4f778c
fix
yanjiaxin534 Sep 19, 2025
9c50fba
try to use cert manager
yanjiaxin534 Sep 19, 2025
2390cd0
cert provider fix
yanjiaxin534 Sep 20, 2025
a350525
add secret check in create cert
yanjiaxin534 Sep 21, 2025
e44cdf3
fix integration test1
yanjiaxin534 Sep 19, 2025
ace1d5d
fix test
yanjiaxin534 Sep 22, 2025
127b54e
fix ut
yanjiaxin534 Sep 22, 2025
dd5b7a1
refine createcert logic duration will from properties
yanjiaxin534 Sep 22, 2025
f186d06
fix type
yanjiaxin534 Sep 22, 2025
d7d0d92
add go mod
yanjiaxin534 Sep 22, 2025
bf49c08
fix test
yanjiaxin534 Sep 22, 2025
5714575
refine cert provider init
yanjiaxin534 Sep 22, 2025
8644d5f
remove cert provider nil check in solution vendor
yanjiaxin534 Sep 22, 2025
29d8d73
fix series number
yanjiaxin534 Sep 22, 2025
3f92994
delete no use
yanjiaxin534 Sep 22, 2025
c0aaea1
recover helper test
yanjiaxin534 Sep 22, 2025
1341c66
refactor cert provider location
yanjiaxin534 Sep 22, 2025
4bb39a5
fix symphony api json
yanjiaxin534 Sep 23, 2025
2d85e4c
get duration from provider config
yanjiaxin534 Sep 23, 2025
dd4abb7
Fix ut
yanjiaxin534 Sep 23, 2025
27b7948
add coa go mod
yanjiaxin534 Sep 23, 2025
3b263a9
fix test
yanjiaxin534 Sep 23, 2025
fa20723
add some retry time
yanjiaxin534 Sep 23, 2025
935df65
add retry
yanjiaxin534 Sep 23, 2025
c2e69ca
add false remove
yanjiaxin534 Sep 23, 2025
c2c987e
fix rust bug
yanjiaxin534 Sep 23, 2025
a6042ec
target & solution vendor & manager refacting
yanjiaxin534 Sep 24, 2025
61c62bb
no get cert provider
yanjiaxin534 Sep 24, 2025
b5e91c0
improve script
yanjiaxin534 Sep 24, 2025
2b95a6e
no need cert provider config
yanjiaxin534 Sep 24, 2025
f7621db
refine function expose
yanjiaxin534 Sep 24, 2025
86297ca
remove get cert provider
yanjiaxin534 Sep 24, 2025
b4efd62
refine dns commen name
yanjiaxin534 Sep 24, 2025
14ca85f
no need remote target name now
yanjiaxin534 Sep 24, 2025
f97352c
fix script
yanjiaxin534 Sep 24, 2025
71bbfc7
fix
yanjiaxin534 Sep 24, 2025
fc7e1e7
refine time const for cert
yanjiaxin534 Sep 24, 2025
2895e9d
use service name as common name
yanjiaxin534 Sep 25, 2025
fa66dd4
fix test
yanjiaxin534 Sep 25, 2025
0e6f580
improve error state
yanjiaxin534 Sep 25, 2025
3d0a757
add vendor context to solution manager in test
yanjiaxin534 Sep 25, 2025
f8fc0b6
fix test
yanjiaxin534 Sep 25, 2025
06f1319
add cert provider to ut
yanjiaxin534 Sep 25, 2025
fd518b9
fix test
yanjiaxin534 Sep 26, 2025
214f646
refine name
yanjiaxin534 Sep 28, 2025
3642797
fix k8s config
yanjiaxin534 Sep 28, 2025
830ad3a
fix test
yanjiaxin534 Sep 28, 2025
189109c
remove no use code
yanjiaxin534 Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions api/pkg/apis/v1alpha1/managers/solution/solution-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/eclipse-symphony/symphony/api/pkg/apis/v1alpha1/managers/solution/metrics"
"github.com/eclipse-symphony/symphony/api/pkg/apis/v1alpha1/model"
sp "github.com/eclipse-symphony/symphony/api/pkg/apis/v1alpha1/providers"
certProvider "github.com/eclipse-symphony/symphony/api/pkg/apis/v1alpha1/providers/cert"
tgt "github.com/eclipse-symphony/symphony/api/pkg/apis/v1alpha1/providers/target"
api_utils "github.com/eclipse-symphony/symphony/api/pkg/apis/v1alpha1/utils"
"github.com/eclipse-symphony/symphony/coa/pkg/apis/v1alpha2"
Expand Down Expand Up @@ -68,6 +69,8 @@ const (
type SolutionManager struct {
SummaryManager
TargetProviders map[string]tgt.ITargetProvider
CertProvider certProvider.ICertProvider
certProviderConfig map[string]interface{}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why you need to keep certProviderConfig here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I fix it now.

ConfigProvider config.IExtConfigProvider
SecretProvider secret.ISecretProvider
KeyLockProvider keylock.IKeyLockProvider
Expand Down Expand Up @@ -118,6 +121,21 @@ func (s *SolutionManager) Init(context *contexts.VendorContext, config managers.
return err
}

// Initialize cert provider
if certProviderInstance, exists := providers["working-cert"]; exists {
if cp, ok := certProviderInstance.(certProvider.ICertProvider); ok {
s.CertProvider = cp
// Try to get config from provider instance if possible
if providerCfg, ok := certProviderInstance.(interface{ Config() map[string]interface{} }); ok {
s.certProviderConfig = providerCfg.Config()
} else if providerCfg, ok := certProviderInstance.(interface{ GetConfig() map[string]interface{} }); ok {
s.certProviderConfig = providerCfg.GetConfig()
}
} else {
return fmt.Errorf("working-cert provider does not implement ICertProvider interface")
}
}

if v, ok := config.Properties["isTarget"]; ok {
b, err := strconv.ParseBool(v)
if err == nil || b {
Expand Down Expand Up @@ -159,6 +177,77 @@ func (s *SolutionManager) Init(context *contexts.VendorContext, config managers.

return nil
}

// GetCertProvider returns the cert provider instance for certificate management operations
func (s *SolutionManager) GetCertProvider() certProvider.ICertProvider {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where did you use this function?

return s.CertProvider
}

// SafeCreateWorkingCert creates a working certificate with validation checks
// It validates that the certificate doesn't exist before creation and verifies creation success after
func (s *SolutionManager) SafeCreateWorkingCert(ctx context.Context, certID string, request certProvider.CertRequest) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we name it as SafeXXX?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix it now

if s.CertProvider == nil {
return fmt.Errorf("cert provider not initialized")
}

// Pre-creation validation: check if certificate already exists
log.InfofCtx(ctx, " M (Solution): validating certificate %s doesn't exist before creation", certID)
_, err := s.CertProvider.GetCert(ctx, certID, request.Namespace)
if err == nil {
log.InfofCtx(ctx, " M (Solution): certificate %s already exists, skipping creation", certID)
return nil
}

// Create the certificate
log.InfofCtx(ctx, " M (Solution): creating working certificate %s", certID)
err = s.CertProvider.CreateCert(ctx, request)
if err != nil {
return fmt.Errorf("failed to create certificate %s: %v", certID, err)
}

// Post-creation validation: verify certificate was created successfully
log.InfofCtx(ctx, " M (Solution): validating certificate %s was created successfully", certID)
_, err = s.CertProvider.GetCert(ctx, certID, request.Namespace)
if err != nil {
return fmt.Errorf("certificate %s creation validation failed, certificate not found after creation: %v", certID, err)
}

log.InfofCtx(ctx, " M (Solution): working certificate %s created and validated successfully", certID)
return nil
}

// SafeDeleteWorkingCert deletes a working certificate with validation checks
// It validates that the certificate exists before deletion and verifies deletion success after
func (s *SolutionManager) SafeDeleteWorkingCert(ctx context.Context, certID string, namespace string) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we name it as SafeXXX?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix it now

if s.CertProvider == nil {
return fmt.Errorf("cert provider not initialized")
}

// Pre-deletion validation: check if certificate exists
log.InfofCtx(ctx, " M (Solution): validating certificate %s exists before deletion", certID)
_, err := s.CertProvider.GetCert(ctx, certID, namespace)
if err != nil {
return fmt.Errorf("certificate %s not found, cannot delete: %v", certID, err)
}

// Delete the certificate
log.InfofCtx(ctx, " M (Solution): deleting working certificate %s", certID)
err = s.CertProvider.DeleteCert(ctx, certID, namespace)
if err != nil {
return fmt.Errorf("failed to delete certificate %s: %v", certID, err)
}

// Post-deletion validation: verify certificate was deleted successfully
log.InfofCtx(ctx, " M (Solution): validating certificate %s was deleted successfully", certID)
_, err = s.CertProvider.GetCert(ctx, certID, namespace)
if err == nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is strange. In this case, you should get the NotFound error and the first cert object should be nil.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check is for delete failed

return fmt.Errorf("certificate %s deletion validation failed, certificate still exists after deletion", certID)
}

log.InfofCtx(ctx, " M (Solution): working certificate %s deleted and validated successfully", certID)
return nil
}

func (s *SolutionManager) AsyncReconcile(ctx context.Context, deployment model.DeploymentSpec, remove bool, namespace string, targetName string) (model.SummarySpec, error) {
lockName := api_utils.GenerateKeyLockName(namespace, deployment.Instance.ObjectMeta.Name)
s.KeyLockProvider.Lock(lockName)
Expand Down Expand Up @@ -1895,3 +1984,16 @@ func (s *SolutionManager) getOperationState(ctx context.Context, operationId str
}
return ret, err
}

// CreateCertRequest creates a certificate request with required fields, letting the cert provider use its configured defaults for Duration and RenewBefore
func (s *SolutionManager) CreateCertRequest(targetName string, namespace string) certProvider.CertRequest {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why you need to expose this function? can you just integrate with SafeCreateWorkingCert?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we don't need it, fix it now

// Create request with required fields - provider will use its configured defaults for Duration and RenewBefore only
return certProvider.CertRequest{
TargetName: targetName,

@iwangjintian iwangjintian Sep 29, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be cert name rather than targetname.
The cert request does not only work for remote agent cert scenarios

Namespace: namespace,
CommonName: "symphony-service", // Required field

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define the const value in the front of the file.

IssuerName: "symphony-ca-issuer", // Required field
DNSNames: []string{targetName, fmt.Sprintf("%s.%s", targetName, namespace)},
// Duration and RenewBefore will use provider defaults
}
}
10 changes: 10 additions & 0 deletions api/pkg/apis/v1alpha1/managers/targets/targets-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/eclipse-symphony/symphony/api/constants"
"github.com/eclipse-symphony/symphony/api/pkg/apis/v1alpha1/model"
certProvider "github.com/eclipse-symphony/symphony/api/pkg/apis/v1alpha1/providers/cert"
"github.com/eclipse-symphony/symphony/api/pkg/apis/v1alpha1/validation"
"github.com/eclipse-symphony/symphony/coa/pkg/apis/v1alpha2"
"github.com/eclipse-symphony/symphony/coa/pkg/apis/v1alpha2/contexts"
Expand All @@ -37,6 +38,7 @@ type TargetsManager struct {
needValidate bool
TargetValidator validation.TargetValidator
SecretProvider secret.ISecretProvider
CertProvider certProvider.ICertProvider
}

func (s *TargetsManager) Init(context *contexts.VendorContext, config managers.ManagerConfig, providers map[string]providers.IProvider) error {
Expand All @@ -61,6 +63,9 @@ func (s *TargetsManager) Init(context *contexts.VendorContext, config managers.M
if c, ok := p.(secret.ISecretProvider); ok {
s.SecretProvider = c
}
if c, ok := p.(certProvider.ICertProvider); ok {
s.CertProvider = c
}
}

return nil
Expand Down Expand Up @@ -308,3 +313,8 @@ func (t *TargetsManager) targetInstanceLookup(ctx context.Context, name string,
}
return len(instanceList) > 0, nil
}

// GetCertProvider returns the certificate provider for read-only access to certificates
func (t *TargetsManager) GetCertProvider() certProvider.ICertProvider {
return t.CertProvider
}
1 change: 1 addition & 0 deletions api/pkg/apis/v1alpha1/model/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type DeploymentSpec struct {
Hash string `json:"hash,omitempty"`
IsDryRun bool `json:"isDryRun,omitempty"`
IsInActive bool `json:"isInActive,omitempty"`
RemoteTargetName string `json:"remoteTargetName,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we need additional field? Can you use targets field?

}

func (d DeploymentSpec) GetComponentSlice() []ComponentSpec {
Expand Down
56 changes: 56 additions & 0 deletions api/pkg/apis/v1alpha1/providers/cert/cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
* SPDX-License-Identifier: MIT
*/

package cert

import (
"context"
"time"
)

// ICertProvider defines the interface for certificate management
type ICertProvider interface {
// CreateCert creates a certificate for the specified target
CreateCert(ctx context.Context, req CertRequest) error

// DeleteCert deletes the certificate for the specified target
DeleteCert(ctx context.Context, targetName, namespace string) error

// GetCert retrieves the certificate for the specified target (read-only)
GetCert(ctx context.Context, targetName, namespace string) (*CertResponse, error)

// CheckCertStatus checks if the certificate is ready and valid
CheckCertStatus(ctx context.Context, targetName, namespace string) (*CertStatus, error)
}

// CertRequest represents a certificate creation request
type CertRequest struct {
TargetName string `json:"targetName"`
Namespace string `json:"namespace"`
Duration time.Duration `json:"duration"`
RenewBefore time.Duration `json:"renewBefore"`
CommonName string `json:"commonName"`
DNSNames []string `json:"dnsNames"`
IssuerName string `json:"issuerName"`
ServiceName string `json:"serviceName"`
}

// CertResponse represents the certificate data
type CertResponse struct {
PublicKey string `json:"publicKey"`
PrivateKey string `json:"privateKey"`
ExpiresAt time.Time `json:"expiresAt"`
SerialNumber string `json:"serialNumber"`
}

// CertStatus represents the certificate status
type CertStatus struct {
Ready bool `json:"ready"`
Reason string `json:"reason"`
Message string `json:"message"`
LastUpdate time.Time `json:"lastUpdate"`
NextRenewal time.Time `json:"nextRenewal"`
}
Loading
Loading