Files
paralus/pkg/service/bootstrap.go
nirav-rafay c66bdc25cd restructure rcloud-base as a single base controller (#37)
* restructure rcloud-base as a single base controller
* updated master.rest
* moved sentry from internal to pkg as it is used by relay
* removing unused rpc and it's dependencies
* Fix usermgmt tests
* Don't redefine variables in rest file
Co-authored-by: Abin Simon <abin.simon@rafay.co>
2022-03-03 17:59:06 +05:30

461 lines
15 KiB
Go

package service
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"time"
"github.com/RafaySystems/rcloud-base/internal/dao"
"github.com/RafaySystems/rcloud-base/internal/models"
"github.com/RafaySystems/rcloud-base/internal/persistence/provider/pg"
"github.com/RafaySystems/rcloud-base/pkg/converter"
"github.com/RafaySystems/rcloud-base/pkg/query"
"github.com/RafaySystems/rcloud-base/pkg/sentry/cryptoutil"
commonv3 "github.com/RafaySystems/rcloud-base/proto/types/commonpb/v3"
"github.com/RafaySystems/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 {
Close() error
// 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
DeleteBoostrapAgent(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 {
dao pg.EntityDAO
bdao dao.BootstrapDao
}
// NewBootstrapService return new bootstrap service
func NewBootstrapService(db *bun.DB) BootstrapService {
edao := pg.NewEntityDAO(db)
return &bootstrapService{
dao: edao,
bdao: dao.NewBootstrapDao(edao),
}
}
func (s *bootstrapService) PatchBootstrapInfra(ctx context.Context, infra *sentry.BootstrapInfra) error {
return s.bdao.CreateOrUpdateBootstrapInfra(ctx, convertToInfraModel(infra))
}
func (s *bootstrapService) GetBootstrapInfra(ctx context.Context, name string) (*sentry.BootstrapInfra, error) {
var bi models.BootstrapInfra
_, err := s.dao.GetByName(ctx, 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 s.bdao.CreateOrUpdateBootstrapAgentTemplate(ctx, &templ)
}
func (s *bootstrapService) GetBootstrapAgentTemplate(ctx context.Context, agentType string) (*sentry.BootstrapAgentTemplate, error) {
var template models.BootstrapAgentTemplate
_, err := s.dao.GetByName(ctx, 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 := s.bdao.GetBootstrapAgentTemplateForToken(ctx, 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 := s.bdao.SelectBootstrapAgentTemplates(ctx, 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 s.bdao.CreateBootstrapAgent(ctx, 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,
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: "BootstapAgentTemplate",
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 := s.bdao.GetBootstrapAgents(ctx, 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 := s.bdao.GetBootstrapAgent(ctx, 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 := s.bdao.SelectBootstrapAgents(ctx, 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 {
return s.bdao.RegisterBootstrapAgent(ctx, token)
}
func (s *bootstrapService) DeleteBoostrapAgent(ctx context.Context, templateRef string, opts ...query.Option) error {
queryOptions := &commonv3.QueryOptions{}
for _, opt := range opts {
opt(queryOptions)
}
err := s.bdao.DeleteBootstrapAgent(ctx, 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.dao.GetInstance().RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
bdb, err := s.bdao.GetBootstrapAgent(ctx, 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 s.bdao.UpdateBootstrapAgent(ctx, bdb, queryOptions)
})
return err
}
func (s *bootstrapService) GetBootstrapAgentForToken(ctx context.Context, token string) (*sentry.BootstrapAgent, error) {
ba, err := s.bdao.GetBootstrapAgentForToken(ctx, 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 := s.bdao.GetBootstrapAgentTemplateForHost(ctx, 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 := s.bdao.GetBootstrapAgentCountForClusterID(ctx, 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 := s.bdao.GetBootstrapAgentForClusterID(ctx, 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")
}
func (s *bootstrapService) Close() error {
return s.dao.Close()
}