Files
paralus/pkg/service/bootstrap.go
2022-03-24 12:42:59 +05:30

453 lines
15 KiB
Go

package service
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"time"
"github.com/RafayLabs/rcloud-base/internal/dao"
"github.com/RafayLabs/rcloud-base/internal/models"
"github.com/RafayLabs/rcloud-base/pkg/converter"
"github.com/RafayLabs/rcloud-base/pkg/query"
"github.com/RafayLabs/rcloud-base/pkg/sentry/cryptoutil"
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
"github.com/RafayLabs/rcloud-base/proto/types/sentry"
"github.com/google/uuid"
"github.com/uptrace/bun"
"google.golang.org/protobuf/types/known/timestamppb"
)
var KEKFunc cryptoutil.PasswordFunc
// BootstrapService is the interface for bootstrap operations
type BootstrapService interface {
// bootstrap infra methods
PatchBootstrapInfra(ctx context.Context, infra *sentry.BootstrapInfra) error
GetBootstrapInfra(ctx context.Context, name string) (*sentry.BootstrapInfra, error)
// bootstrap template methods
PatchBootstrapAgentTemplate(ctx context.Context, template *sentry.BootstrapAgentTemplate) error
GetBootstrapAgentTemplate(ctx context.Context, name string) (*sentry.BootstrapAgentTemplate, error)
GetBootstrapAgentTemplateForToken(ctx context.Context, token string) (*sentry.BootstrapAgentTemplate, error)
GetBootstrapAgentTemplateForHost(ctx context.Context, host string) (*sentry.BootstrapAgentTemplate, error)
SelectBootstrapAgentTemplates(ctx context.Context, opts ...query.Option) (*sentry.BootstrapAgentTemplateList, error)
// bootstrap agent methods
CreateBootstrapAgent(ctx context.Context, agent *sentry.BootstrapAgent) error
GetBootstrapAgent(ctx context.Context, templateRef string, opts ...query.Option) (*sentry.BootstrapAgent, error)
GetBootstrapAgents(ctx context.Context, templateRef string, opts ...query.Option) (*sentry.BootstrapAgentList, error)
GetBootstrapAgentForToken(ctx context.Context, token string) (*sentry.BootstrapAgent, error)
GetBootstrapAgentCountForClusterID(ctx context.Context, clusterID string, orgID string) (int, error)
GetBootstrapAgentForClusterID(ctx context.Context, clusterID string, orgID string) (*sentry.BootstrapAgent, error)
SelectBootstrapAgents(ctx context.Context, templateRef string, opts ...query.Option) (*sentry.BootstrapAgentList, error)
RegisterBootstrapAgent(ctx context.Context, token string) error
DeleteBootstrapAgent(ctx context.Context, templateRef string, opts ...query.Option) error
PatchBootstrapAgent(ctx context.Context, ba *sentry.BootstrapAgent, templateRef string, opts ...query.Option) error
}
// bootstrapService implements BootstrapService
type bootstrapService struct {
db *bun.DB
}
// NewBootstrapService return new bootstrap service
func NewBootstrapService(db *bun.DB) BootstrapService {
return &bootstrapService{db}
}
func (s *bootstrapService) PatchBootstrapInfra(ctx context.Context, infra *sentry.BootstrapInfra) error {
return dao.CreateOrUpdateBootstrapInfra(ctx, s.db, convertToInfraModel(infra))
}
func (s *bootstrapService) GetBootstrapInfra(ctx context.Context, name string) (*sentry.BootstrapInfra, error) {
var bi models.BootstrapInfra
_, err := dao.GetByName(ctx, s.db, name, &bi)
if err != nil {
return nil, err
}
return prepareInfraResponse(&bi), nil
}
func (s *bootstrapService) PatchBootstrapAgentTemplate(ctx context.Context, template *sentry.BootstrapAgentTemplate) error {
templ := models.BootstrapAgentTemplate{
Name: template.Metadata.Name,
DisplayName: template.Metadata.DisplayName,
InfraRef: template.Spec.InfraRef,
ModifiedAt: time.Now(),
Labels: converter.ConvertToJsonRawMessage(template.Metadata.Labels),
Annotations: converter.ConvertToJsonRawMessage(template.Metadata.Annotations),
AutoRegister: template.Spec.AutoRegister,
AutoApprove: template.Spec.AutoApprove,
TemplateType: sentry.BootstrapAgentTemplateType_name[int32(template.Spec.TemplateType)],
IgnoreMultipleRegister: template.Spec.IgnoreMultipleRegister,
InclusterTemplate: template.Spec.InClusterTemplate,
OutofclusterTemplate: template.Spec.OutOfClusterTemplate,
Token: template.Spec.Token,
Hosts: converter.ConvertToJsonRawMessage(template.Spec.Hosts),
CreatedAt: time.Now(),
}
return dao.CreateOrUpdateBootstrapAgentTemplate(ctx, s.db, &templ)
}
func (s *bootstrapService) GetBootstrapAgentTemplate(ctx context.Context, agentType string) (*sentry.BootstrapAgentTemplate, error) {
var template models.BootstrapAgentTemplate
_, err := dao.GetByName(ctx, s.db, agentType, &template)
if err != nil {
return nil, err
}
return prepareTemplateResponse(&template), nil
}
func (s *bootstrapService) GetBootstrapAgentTemplateForToken(ctx context.Context, token string) (*sentry.BootstrapAgentTemplate, error) {
bat, err := dao.GetBootstrapAgentTemplateForToken(ctx, s.db, token)
if err != nil {
return nil, err
}
return prepareTemplateResponse(bat), nil
}
func (s *bootstrapService) SelectBootstrapAgentTemplates(ctx context.Context, opts ...query.Option) (*sentry.BootstrapAgentTemplateList, error) {
queryOptions := &commonv3.QueryOptions{}
for _, opt := range opts {
opt(queryOptions)
}
batl, count, err := dao.SelectBootstrapAgentTemplates(ctx, s.db, queryOptions)
if err != nil {
return nil, err
}
ret := &sentry.BootstrapAgentTemplateList{Metadata: &commonv3.ListMetadata{
Count: int64(count),
}}
for _, bat := range batl {
ret.Items = append(ret.Items, prepareTemplateResponse(&bat))
}
return ret, nil
}
func (s *bootstrapService) CreateBootstrapAgent(ctx context.Context, agent *sentry.BootstrapAgent) error {
ba := convertToAgentModel(agent)
ba.CreatedAt = time.Now()
return dao.CreateBootstrapAgent(ctx, s.db, ba)
}
func convertToAgentModel(agent *sentry.BootstrapAgent) *models.BootstrapAgent {
agentMdl := &models.BootstrapAgent{
Name: agent.Metadata.Name,
TemplateRef: agent.Spec.TemplateRef,
AgentMode: agent.Spec.AgentMode.String(),
DisplayName: agent.Metadata.DisplayName,
Labels: converter.ConvertToJsonRawMessage(agent.Metadata.Labels),
Annotations: converter.ConvertToJsonRawMessage(agent.Metadata.Annotations),
Token: agent.Spec.Token,
}
if orgId, err := uuid.Parse(agent.Metadata.Organization); err == nil {
agentMdl.OrganizationId = orgId
}
if partId, err := uuid.Parse(agent.Metadata.Partner); err == nil {
agentMdl.PartnerId = partId
}
if projId, err := uuid.Parse(agent.Metadata.Project); err == nil {
agentMdl.ProjectId = projId
}
return agentMdl
}
func convertToInfraModel(infra *sentry.BootstrapInfra) *models.BootstrapInfra {
return &models.BootstrapInfra{
Name: infra.Metadata.Name,
ModifiedAt: time.Now(),
CaCert: infra.Spec.CaCert,
CaKey: infra.Spec.CaKey,
DisplayName: infra.Metadata.DisplayName,
Labels: converter.ConvertToJsonRawMessage(infra.Metadata.Labels),
Annotations: converter.ConvertToJsonRawMessage(infra.Metadata.Annotations),
}
}
func prepareAgentResponse(agent *models.BootstrapAgent) *sentry.BootstrapAgent {
var lbls map[string]string
if agent.Labels != nil {
json.Unmarshal(agent.Labels, &lbls)
}
var ann map[string]string
if agent.Annotations != nil {
json.Unmarshal(agent.Annotations, &ann)
}
ba := &sentry.BootstrapAgent{
Kind: "BootstrapAgent",
Metadata: &commonv3.Metadata{
Name: agent.Name,
DisplayName: agent.DisplayName,
Description: agent.DisplayName,
ModifiedAt: timestamppb.New(agent.ModifiedAt),
Labels: lbls,
Annotations: ann,
},
Spec: &sentry.BootstrapAgentSpec{
Token: agent.Token,
TemplateRef: agent.TemplateRef,
AgentMode: sentry.BootstrapAgentMode(sentry.BootstrapAgentMode_value[agent.AgentMode]),
},
Status: &sentry.BootStrapAgentStatus{
TokenState: sentry.BootstrapAgentState(sentry.BootstrapAgentMode_value[agent.TokenState]),
IpAddress: agent.IPAddress,
LastCheckedIn: timestamppb.New(agent.LastCheckedIn),
Fingerprint: agent.Fingerprint,
},
}
return ba
}
func prepareInfraResponse(infra *models.BootstrapInfra) *sentry.BootstrapInfra {
var lbls map[string]string
if infra.Labels != nil {
json.Unmarshal(infra.Labels, &lbls)
}
var ann map[string]string
if infra.Annotations != nil {
json.Unmarshal(infra.Annotations, &ann)
}
bi := &sentry.BootstrapInfra{
Kind: "BootstrapInfra",
Metadata: &commonv3.Metadata{
Name: infra.Name,
DisplayName: infra.DisplayName,
ModifiedAt: timestamppb.New(infra.ModifiedAt),
Labels: lbls,
Annotations: ann,
},
Spec: &sentry.BootstrapInfraSpec{
CaCert: infra.CaCert,
CaKey: infra.CaKey,
},
}
return bi
}
func prepareTemplateResponse(template *models.BootstrapAgentTemplate) *sentry.BootstrapAgentTemplate {
var lbls map[string]string
if template.Labels != nil {
json.Unmarshal(template.Labels, &lbls)
}
var ann map[string]string
if template.Annotations != nil {
json.Unmarshal(template.Annotations, &ann)
}
var hosts []*sentry.BootstrapTemplateHost
if template.Hosts != nil {
json.Unmarshal(template.Hosts, &hosts)
}
templResp := sentry.BootstrapAgentTemplate{
Kind: "BootstrapAgentTemplate",
Metadata: &commonv3.Metadata{
Name: template.Name,
DisplayName: template.DisplayName,
Labels: lbls,
Annotations: ann,
ModifiedAt: timestamppb.New(template.ModifiedAt),
},
Spec: &sentry.BootstrapAgentTemplateSpec{
InfraRef: template.InfraRef,
AutoRegister: template.AutoRegister,
AutoApprove: template.AutoApprove,
IgnoreMultipleRegister: template.IgnoreMultipleRegister,
TemplateType: sentry.BootstrapAgentTemplateType(sentry.BootstrapAgentTemplateType_value[template.TemplateType]),
Token: template.Token,
Hosts: hosts,
InClusterTemplate: template.InclusterTemplate,
OutOfClusterTemplate: template.OutofclusterTemplate,
},
}
return &templResp
}
func (s *bootstrapService) GetBootstrapAgents(ctx context.Context, templateRef string, opts ...query.Option) (ret *sentry.BootstrapAgentList, err error) {
queryOptions := &commonv3.QueryOptions{}
for _, opt := range opts {
opt(queryOptions)
}
agl, count, err := dao.GetBootstrapAgents(ctx, s.db, queryOptions, templateRef)
if err != nil {
return nil, err
}
ret = new(sentry.BootstrapAgentList)
ret.Metadata = &commonv3.ListMetadata{
Count: int64(count),
}
for _, ag := range agl {
ret.Items = append(ret.Items, prepareAgentResponse(&ag))
}
return
}
func (s *bootstrapService) GetBootstrapAgent(ctx context.Context, templateRef string, opts ...query.Option) (*sentry.BootstrapAgent, error) {
queryOptions := &commonv3.QueryOptions{}
for _, opt := range opts {
opt(queryOptions)
}
ba, err := dao.GetBootstrapAgent(ctx, s.db, templateRef, queryOptions)
if err != nil {
return nil, err
}
return prepareAgentResponse(ba), nil
}
func (s *bootstrapService) SelectBootstrapAgents(ctx context.Context, templateRef string, opts ...query.Option) (ret *sentry.BootstrapAgentList, err error) {
queryOptions := &commonv3.QueryOptions{}
for _, opt := range opts {
opt(queryOptions)
}
agl, count, err := dao.SelectBootstrapAgents(ctx, s.db, templateRef, queryOptions)
if err != nil {
return nil, err
}
ret = new(sentry.BootstrapAgentList)
ret.Metadata = &commonv3.ListMetadata{
Count: int64(count),
}
for _, ag := range agl {
ret.Items = append(ret.Items, prepareAgentResponse(&ag))
}
return
}
func (s *bootstrapService) RegisterBootstrapAgent(ctx context.Context, token string) error {
err := s.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
return dao.RegisterBootstrapAgent(ctx, tx, token)
})
return err
}
func (s *bootstrapService) DeleteBootstrapAgent(ctx context.Context, templateRef string, opts ...query.Option) error {
queryOptions := &commonv3.QueryOptions{}
for _, opt := range opts {
opt(queryOptions)
}
err := dao.DeleteBootstrapAgent(ctx, s.db, templateRef, queryOptions)
return err
}
func (s *bootstrapService) PatchBootstrapAgent(ctx context.Context, ba *sentry.BootstrapAgent, templateRef string, opts ...query.Option) error {
queryOptions := &commonv3.QueryOptions{}
for _, opt := range opts {
opt(queryOptions)
}
err := s.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
bdb, err := dao.GetBootstrapAgent(ctx, s.db, templateRef, queryOptions)
if err != nil {
return err
}
if bdb.TokenState > sentry.BootstrapAgentState_NotSet.String() {
bdb.TokenState = ba.Status.TokenState.String()
}
if ba.Status != nil {
if ba.Status.IpAddress != "" {
bdb.IPAddress = ba.Status.IpAddress
} else {
bdb.IPAddress = ""
}
if !ba.Status.LastCheckedIn.AsTime().IsZero() {
bdb.LastCheckedIn = ba.Status.LastCheckedIn.AsTime()
}
if ba.Status.Fingerprint != "" {
bdb.Fingerprint = ba.Status.Fingerprint
} else {
bdb.Fingerprint = ""
}
}
bdb.ModifiedAt = time.Now()
bdb.DisplayName = ba.Metadata.DisplayName
return dao.UpdateBootstrapAgent(ctx, s.db, bdb, queryOptions)
})
return err
}
func (s *bootstrapService) GetBootstrapAgentForToken(ctx context.Context, token string) (*sentry.BootstrapAgent, error) {
ba, err := dao.GetBootstrapAgentForToken(ctx, s.db, token)
if err != nil {
return nil, err
}
return prepareAgentResponse(ba), nil
}
func (s *bootstrapService) GetBootstrapAgentTemplateForHost(ctx context.Context, host string) (*sentry.BootstrapAgentTemplate, error) {
bat, err := dao.GetBootstrapAgentTemplateForHost(ctx, s.db, host)
if err != nil {
return nil, err
}
return prepareTemplateResponse(bat), nil
}
func (s *bootstrapService) GetBootstrapAgentCountForClusterID(ctx context.Context, clusterID string, orgID string) (int, error) {
count, err := dao.GetBootstrapAgentCountForClusterID(ctx, s.db, clusterID, uuid.MustParse(orgID))
if err != nil {
return 0, err
}
if count <= 0 {
return 0, fmt.Errorf("invalid request")
}
return count, nil
}
func (s *bootstrapService) GetBootstrapAgentForClusterID(ctx context.Context, clusterID string, orgID string) (*sentry.BootstrapAgent, error) {
ba, err := dao.GetBootstrapAgentForClusterID(ctx, s.db, clusterID, uuid.MustParse(orgID))
if err != nil || ba == nil {
return nil, err
}
return prepareAgentResponse(ba), nil
}
func (s *bootstrapService) GetRelayAgent(ctx context.Context, ClusterScope string, opts ...query.Option) (*sentry.BootstrapAgent, error) {
queryOptions := &commonv3.QueryOptions{}
for _, opt := range opts {
opt(queryOptions)
}
bal, err := s.SelectBootstrapAgents(ctx, queryOptions.Name,
query.WithOrganizationID(queryOptions.Organization),
query.WithPartnerID(queryOptions.Partner),
)
if err != nil {
_log.Infow("failed to get default bootstrap agent list", "cluster", ClusterScope, "error", err)
return nil, err
}
if bal != nil && bal.Metadata.Count > 0 {
var ba sentry.BootstrapAgent
found := false
// match labels
for _, b := range bal.Items {
_log.Infow("match", "ClusterScope", ClusterScope, "DisplayName", b.Metadata.DisplayName)
if "cluster/"+b.Metadata.DisplayName == ClusterScope {
found = true
ba = *b
break
}
}
if found {
// found bootstrap relay agent for cluster as per the association
return &ba, nil
} else {
_log.Infow("did not find relay bootstrap agent for", "cluster", ClusterScope, "template", queryOptions.Name)
}
}
_log.Infow("did not find relay bootstrap agent for", "cluster", ClusterScope, "template", queryOptions.Name)
return nil, fmt.Errorf("failed to get relay agent")
}