Fix move tpl to global (#848)
This commit is contained in:
commit
54080efdf3
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
# Only allow releases on the main branch (for 3k)
|
# Only allow releases on the main branch (for 3k)
|
||||||
# releases for 2k are published manaually.
|
# releases for 2k are published manually.
|
||||||
branches:
|
branches:
|
||||||
- name: main
|
- name: main
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
entities "github.com/OliveTin/OliveTin/internal/entities"
|
entities "github.com/OliveTin/OliveTin/internal/entities"
|
||||||
executor "github.com/OliveTin/OliveTin/internal/executor"
|
executor "github.com/OliveTin/OliveTin/internal/executor"
|
||||||
installationinfo "github.com/OliveTin/OliveTin/internal/installationinfo"
|
installationinfo "github.com/OliveTin/OliveTin/internal/installationinfo"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/tpl"
|
||||||
connectproto "go.akshayshah.org/connectproto"
|
connectproto "go.akshayshah.org/connectproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -709,8 +710,8 @@ func (api *oliveTinAPI) DumpVars(ctx ctx.Context, req *connect.Request[apiv1.Dum
|
||||||
return connect.NewResponse(res), nil
|
return connect.NewResponse(res), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonstring, _ := json.MarshalIndent(entities.GetAll(), "", " ")
|
jsonstring, _ := json.MarshalIndent(tpl.GetNewGeneralTemplateContext(), "", " ")
|
||||||
fmt.Printf("%s", &jsonstring)
|
fmt.Printf("%s", jsonstring)
|
||||||
|
|
||||||
res.Alert = "Dumping variables has been enabled in the configuration. Please set InsecureAllowDumpVars = false again after you don't need it anymore"
|
res.Alert = "Dumping variables has been enabled in the configuration. Please set InsecureAllowDumpVars = false again after you don't need it anymore"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
config "github.com/OliveTin/OliveTin/internal/config"
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
entities "github.com/OliveTin/OliveTin/internal/entities"
|
entities "github.com/OliveTin/OliveTin/internal/entities"
|
||||||
executor "github.com/OliveTin/OliveTin/internal/executor"
|
executor "github.com/OliveTin/OliveTin/internal/executor"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/tpl"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DashboardRenderRequest struct {
|
type DashboardRenderRequest struct {
|
||||||
|
|
@ -66,7 +67,7 @@ func evaluateEnabledExpression(action *config.Action, entity *entities.Entity) b
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
result := entities.ParseTemplateWith(action.EnabledExpression, entity)
|
result := tpl.ParseTemplateWith(action.EnabledExpression, entity)
|
||||||
result = strings.TrimSpace(result)
|
result = strings.TrimSpace(result)
|
||||||
|
|
||||||
if result == "" {
|
if result == "" {
|
||||||
|
|
@ -105,6 +106,16 @@ func evaluateResultValue(result string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDefaultValue(cfgArg config.ActionArgument, entity *entities.Entity) string {
|
||||||
|
defaultValue := cfgArg.Default
|
||||||
|
|
||||||
|
if defaultValue != "" {
|
||||||
|
defaultValue = tpl.ParseTemplateWith(defaultValue, entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
func buildAction(actionBinding *executor.ActionBinding, rr *DashboardRenderRequest) *apiv1.Action {
|
func buildAction(actionBinding *executor.ActionBinding, rr *DashboardRenderRequest) *apiv1.Action {
|
||||||
action := actionBinding.Action
|
action := actionBinding.Action
|
||||||
|
|
||||||
|
|
@ -120,8 +131,8 @@ func buildAction(actionBinding *executor.ActionBinding, rr *DashboardRenderReque
|
||||||
|
|
||||||
btn := apiv1.Action{
|
btn := apiv1.Action{
|
||||||
BindingId: actionBinding.ID,
|
BindingId: actionBinding.ID,
|
||||||
Title: entities.ParseTemplateWith(action.Title, actionBinding.Entity),
|
Title: tpl.ParseTemplateWith(action.Title, actionBinding.Entity),
|
||||||
Icon: entities.ParseTemplateWith(action.Icon, actionBinding.Entity),
|
Icon: tpl.ParseTemplateWith(action.Icon, actionBinding.Entity),
|
||||||
CanExec: aclCanExec && enabledExprCanExec,
|
CanExec: aclCanExec && enabledExprCanExec,
|
||||||
PopupOnStart: action.PopupOnStart,
|
PopupOnStart: action.PopupOnStart,
|
||||||
Order: int32(actionBinding.ConfigOrder),
|
Order: int32(actionBinding.ConfigOrder),
|
||||||
|
|
@ -135,7 +146,7 @@ func buildAction(actionBinding *executor.ActionBinding, rr *DashboardRenderReque
|
||||||
Title: cfgArg.Title,
|
Title: cfgArg.Title,
|
||||||
Type: cfgArg.Type,
|
Type: cfgArg.Type,
|
||||||
Description: cfgArg.Description,
|
Description: cfgArg.Description,
|
||||||
DefaultValue: cfgArg.Default,
|
DefaultValue: getDefaultValue(cfgArg, actionBinding.Entity),
|
||||||
Choices: buildChoices(cfgArg),
|
Choices: buildChoices(cfgArg),
|
||||||
Suggestions: cfgArg.Suggestions,
|
Suggestions: cfgArg.Suggestions,
|
||||||
SuggestionsBrowserKey: cfgArg.SuggestionsBrowserKey,
|
SuggestionsBrowserKey: cfgArg.SuggestionsBrowserKey,
|
||||||
|
|
@ -162,8 +173,8 @@ func buildChoicesEntity(firstChoice config.ActionArgumentChoice, entityTitle str
|
||||||
|
|
||||||
for _, ent := range entList {
|
for _, ent := range entList {
|
||||||
ret = append(ret, &apiv1.ActionArgumentChoice{
|
ret = append(ret, &apiv1.ActionArgumentChoice{
|
||||||
Value: entities.ParseTemplateWith(firstChoice.Value, ent),
|
Value: tpl.ParseTemplateWith(firstChoice.Value, ent),
|
||||||
Title: entities.ParseTemplateWith(firstChoice.Title, ent),
|
Title: tpl.ParseTemplateWith(firstChoice.Title, ent),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,9 +119,9 @@ func TestGetEntities(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupTestEntities() {
|
func setupTestEntities() {
|
||||||
entities.ClearEntities("server")
|
entities.ClearEntitiesOfType("server")
|
||||||
entities.ClearEntities("database")
|
entities.ClearEntitiesOfType("database")
|
||||||
entities.ClearEntities("application")
|
entities.ClearEntitiesOfType("application")
|
||||||
|
|
||||||
entities.AddEntity("server", "zebra", map[string]any{"title": "Server Zebra", "hostname": "zebra.example.com"})
|
entities.AddEntity("server", "zebra", map[string]any{"title": "Server Zebra", "hostname": "zebra.example.com"})
|
||||||
entities.AddEntity("server", "alpha", map[string]any{"title": "Server Alpha", "hostname": "alpha.example.com"})
|
entities.AddEntity("server", "alpha", map[string]any{"title": "Server Alpha", "hostname": "alpha.example.com"})
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
|
apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
|
||||||
config "github.com/OliveTin/OliveTin/internal/config"
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
entities "github.com/OliveTin/OliveTin/internal/entities"
|
entities "github.com/OliveTin/OliveTin/internal/entities"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/tpl"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -23,14 +24,14 @@ func buildEntityFieldsets(entityTitle string, tpl *config.DashboardComponent, rr
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildEntityFieldset(tpl *config.DashboardComponent, ent *entities.Entity, rr *DashboardRenderRequest) *apiv1.DashboardComponent {
|
func buildEntityFieldset(component *config.DashboardComponent, ent *entities.Entity, rr *DashboardRenderRequest) *apiv1.DashboardComponent {
|
||||||
return &apiv1.DashboardComponent{
|
return &apiv1.DashboardComponent{
|
||||||
Title: entities.ParseTemplateWith(tpl.Title, ent),
|
Title: tpl.ParseTemplateWith(component.Title, ent),
|
||||||
Type: "fieldset",
|
Type: "fieldset",
|
||||||
Contents: removeFieldsetIfHasNoLinks(buildEntityFieldsetContents(tpl.Contents, ent, tpl.Entity, rr)),
|
Contents: removeFieldsetIfHasNoLinks(buildEntityFieldsetContents(component.Contents, ent, component.Entity, rr)),
|
||||||
CssClass: entities.ParseTemplateWith(tpl.CssClass, ent),
|
CssClass: tpl.ParseTemplateWith(component.CssClass, ent),
|
||||||
Action: rr.findAction(tpl.Title),
|
Action: rr.findAction(component.Title),
|
||||||
EntityType: tpl.Entity,
|
EntityType: component.Entity,
|
||||||
EntityKey: ent.UniqueKey,
|
EntityKey: ent.UniqueKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,7 +69,7 @@ func buildEntityFieldsetContents(contents []*config.DashboardComponent, ent *ent
|
||||||
|
|
||||||
func cloneItem(subitem *config.DashboardComponent, ent *entities.Entity, entityType string, rr *DashboardRenderRequest) *apiv1.DashboardComponent {
|
func cloneItem(subitem *config.DashboardComponent, ent *entities.Entity, entityType string, rr *DashboardRenderRequest) *apiv1.DashboardComponent {
|
||||||
clone := &apiv1.DashboardComponent{}
|
clone := &apiv1.DashboardComponent{}
|
||||||
clone.CssClass = entities.ParseTemplateWith(subitem.CssClass, ent)
|
clone.CssClass = tpl.ParseTemplateWith(subitem.CssClass, ent)
|
||||||
|
|
||||||
if isLinkType(subitem.Type) {
|
if isLinkType(subitem.Type) {
|
||||||
return cloneLinkItem(subitem, ent, clone, rr)
|
return cloneLinkItem(subitem, ent, clone, rr)
|
||||||
|
|
@ -83,7 +84,7 @@ func isLinkType(itemType string) bool {
|
||||||
|
|
||||||
func cloneLinkItem(subitem *config.DashboardComponent, ent *entities.Entity, clone *apiv1.DashboardComponent, rr *DashboardRenderRequest) *apiv1.DashboardComponent {
|
func cloneLinkItem(subitem *config.DashboardComponent, ent *entities.Entity, clone *apiv1.DashboardComponent, rr *DashboardRenderRequest) *apiv1.DashboardComponent {
|
||||||
clone.Type = "link"
|
clone.Type = "link"
|
||||||
clone.Title = entities.ParseTemplateWith(subitem.Title, ent)
|
clone.Title = tpl.ParseTemplateWith(subitem.Title, ent)
|
||||||
// Prefer an entity-specific action when available, but fall back to a
|
// Prefer an entity-specific action when available, but fall back to a
|
||||||
// non-entity-scoped action with the same title. This allows inline actions
|
// non-entity-scoped action with the same title. This allows inline actions
|
||||||
// defined inside entity dashboards to work without requiring an explicit
|
// defined inside entity dashboards to work without requiring an explicit
|
||||||
|
|
@ -98,7 +99,7 @@ func cloneLinkItem(subitem *config.DashboardComponent, ent *entities.Entity, clo
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneNonLinkItem(subitem *config.DashboardComponent, ent *entities.Entity, entityType string, clone *apiv1.DashboardComponent, rr *DashboardRenderRequest) *apiv1.DashboardComponent {
|
func cloneNonLinkItem(subitem *config.DashboardComponent, ent *entities.Entity, entityType string, clone *apiv1.DashboardComponent, rr *DashboardRenderRequest) *apiv1.DashboardComponent {
|
||||||
clone.Title = entities.ParseTemplateWith(subitem.Title, ent)
|
clone.Title = tpl.ParseTemplateWith(subitem.Title, ent)
|
||||||
clone.Type = subitem.Type
|
clone.Type = subitem.Type
|
||||||
|
|
||||||
if isDirectoryWithEntity(clone.Type, ent, entityType) {
|
if isDirectoryWithEntity(clone.Type, ent, entityType) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
|
apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
|
||||||
config "github.com/OliveTin/OliveTin/internal/config"
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
entities "github.com/OliveTin/OliveTin/internal/entities"
|
entities "github.com/OliveTin/OliveTin/internal/entities"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/tpl"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
@ -236,7 +237,7 @@ func buildDashboardComponentSimpleWithEntity(subitem *config.DashboardComponent,
|
||||||
|
|
||||||
title := subitem.Title
|
title := subitem.Title
|
||||||
if entity != nil {
|
if entity != nil {
|
||||||
title = entities.ParseTemplateWith(subitem.Title, entity)
|
title = tpl.ParseTemplateWith(subitem.Title, entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
newitem := &apiv1.DashboardComponent{
|
newitem := &apiv1.DashboardComponent{
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,7 @@ type Config struct {
|
||||||
Include string `koanf:"include"`
|
Include string `koanf:"include"`
|
||||||
|
|
||||||
sourceFiles []string
|
sourceFiles []string
|
||||||
|
passwordTemplateParser func(string, interface{}) string
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthLocalUsersConfig struct {
|
type AuthLocalUsersConfig struct {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ func (cfg *Config) Sanitize() {
|
||||||
cfg.sanitizeLogLevel()
|
cfg.sanitizeLogLevel()
|
||||||
cfg.sanitizeAuthRequireGuestsToLogin()
|
cfg.sanitizeAuthRequireGuestsToLogin()
|
||||||
cfg.sanitizeLogHistoryPageSize()
|
cfg.sanitizeLogHistoryPageSize()
|
||||||
|
cfg.sanitizeLocalUserPasswords()
|
||||||
|
|
||||||
// log.Infof("cfg %p", cfg)
|
// log.Infof("cfg %p", cfg)
|
||||||
|
|
||||||
|
|
@ -172,6 +173,26 @@ func (cfg *Config) sanitizeLogHistoryPageSize() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPasswordTemplateParser sets the function to use for parsing password templates.
|
||||||
|
// This is called from main.go to avoid import cycles (config can't import entities).
|
||||||
|
func (cfg *Config) SetPasswordTemplateParser(parser func(string, interface{}) string) {
|
||||||
|
cfg.passwordTemplateParser = parser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) sanitizeLocalUserPasswords() {
|
||||||
|
if cfg.passwordTemplateParser == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range cfg.AuthLocalUsers.Users {
|
||||||
|
if user.Password != "" {
|
||||||
|
// Parse password as template to support environment variables and other template values
|
||||||
|
// Note: .CurrentEntity is nil in this context as local users are not entity-bound
|
||||||
|
user.Password = cfg.passwordTemplateParser(user.Password, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getActionID(action *Action) string {
|
func getActionID(action *Action) string {
|
||||||
if action.ID == "" {
|
if action.ID == "" {
|
||||||
return uuid.NewString()
|
return uuid.NewString()
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ func loadEntityFileYaml(filename string, entityname string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSvFromFile(entityname string, data []map[string]any) {
|
func updateSvFromFile(entityname string, data []map[string]any) {
|
||||||
ClearEntities(entityname)
|
ClearEntitiesOfType(entityname)
|
||||||
|
|
||||||
for i, mapp := range data {
|
for i, mapp := range data {
|
||||||
AddEntity(entityname, fmt.Sprintf("%d", i), mapp)
|
AddEntity(entityname, fmt.Sprintf("%d", i), mapp)
|
||||||
|
|
|
||||||
|
|
@ -10,72 +10,31 @@ package entities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/OliveTin/OliveTin/internal/installationinfo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type entityInstancesByKey map[string]*Entity
|
type entityInstancesByKey map[string]*Entity
|
||||||
|
|
||||||
type entitiesByClass map[string]entityInstancesByKey
|
type EntitiesByClass map[string]entityInstancesByKey
|
||||||
|
|
||||||
type variableBase struct {
|
|
||||||
OliveTin installationInfo
|
|
||||||
Entities entitiesByClass
|
|
||||||
|
|
||||||
CurrentEntity interface{}
|
|
||||||
Arguments map[string]string
|
|
||||||
Env map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type installationInfo struct {
|
|
||||||
Build *installationinfo.BuildInfo
|
|
||||||
Runtime *installationinfo.RuntimeInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
contents *variableBase
|
|
||||||
rwmutex = sync.RWMutex{}
|
rwmutex = sync.RWMutex{}
|
||||||
|
entities EntitiesByClass
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rwmutex.Lock()
|
rwmutex.Lock()
|
||||||
|
entities = make(EntitiesByClass, 0)
|
||||||
envMap := make(map[string]string)
|
|
||||||
for _, env := range os.Environ() {
|
|
||||||
parts := strings.SplitN(env, "=", 2)
|
|
||||||
if len(parts) == 2 {
|
|
||||||
envMap[parts[0]] = parts[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contents = &variableBase{
|
|
||||||
OliveTin: installationInfo{
|
|
||||||
Build: installationinfo.Build,
|
|
||||||
Runtime: installationinfo.Runtime,
|
|
||||||
},
|
|
||||||
Entities: make(entitiesByClass, 0),
|
|
||||||
Env: envMap,
|
|
||||||
}
|
|
||||||
|
|
||||||
rwmutex.Unlock()
|
rwmutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAll() *variableBase {
|
func GetEntities() EntitiesByClass {
|
||||||
rwmutex.RLock()
|
|
||||||
defer rwmutex.RUnlock()
|
|
||||||
|
|
||||||
return contents
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetEntities() entitiesByClass {
|
|
||||||
rwmutex.RLock()
|
rwmutex.RLock()
|
||||||
|
|
||||||
copiedEntities := make(entitiesByClass, len(contents.Entities))
|
copiedEntities := make(EntitiesByClass, len(entities))
|
||||||
|
|
||||||
for entityName, entityInstances := range contents.Entities {
|
for entityName, entityInstances := range entities {
|
||||||
copiedInstances := make(entityInstancesByKey, len(entityInstances))
|
copiedInstances := make(entityInstancesByKey, len(entityInstances))
|
||||||
|
|
||||||
for key, entity := range entityInstances {
|
for key, entity := range entityInstances {
|
||||||
|
|
@ -93,7 +52,7 @@ func GetEntityInstances(entityName string) entityInstancesByKey {
|
||||||
rwmutex.RLock()
|
rwmutex.RLock()
|
||||||
defer rwmutex.RUnlock()
|
defer rwmutex.RUnlock()
|
||||||
|
|
||||||
if entities, ok := contents.Entities[entityName]; ok {
|
if entities, ok := entities[entityName]; ok {
|
||||||
copiedInstances := make(entityInstancesByKey, len(entities))
|
copiedInstances := make(entityInstancesByKey, len(entities))
|
||||||
|
|
||||||
for key, entity := range entities {
|
for key, entity := range entities {
|
||||||
|
|
@ -108,11 +67,11 @@ func GetEntityInstances(entityName string) entityInstancesByKey {
|
||||||
func AddEntity(entityName string, entityKey string, data any) {
|
func AddEntity(entityName string, entityKey string, data any) {
|
||||||
rwmutex.Lock()
|
rwmutex.Lock()
|
||||||
|
|
||||||
if _, ok := contents.Entities[entityName]; !ok {
|
if _, ok := entities[entityName]; !ok {
|
||||||
contents.Entities[entityName] = make(entityInstancesByKey, 0)
|
entities[entityName] = make(entityInstancesByKey, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
contents.Entities[entityName][entityKey] = &Entity{
|
entities[entityName][entityKey] = &Entity{
|
||||||
Data: data,
|
Data: data,
|
||||||
UniqueKey: entityKey,
|
UniqueKey: entityKey,
|
||||||
Title: findEntityTitle(data),
|
Title: findEntityTitle(data),
|
||||||
|
|
@ -144,3 +103,10 @@ func findEntityTitle(data any) string {
|
||||||
|
|
||||||
return "Untitled Entity"
|
return "Untitled Entity"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ClearEntitiesOfType(entityType string) {
|
||||||
|
rwmutex.Lock()
|
||||||
|
defer rwmutex.Unlock()
|
||||||
|
|
||||||
|
delete(entities, entityType)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package executor
|
||||||
import (
|
import (
|
||||||
config "github.com/OliveTin/OliveTin/internal/config"
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
"github.com/OliveTin/OliveTin/internal/entities"
|
"github.com/OliveTin/OliveTin/internal/entities"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/tpl"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -75,7 +76,7 @@ func parseSingleExec(a string, values map[string]string, entity *entities.Entity
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return entities.ParseTemplateWithArgs(arg, entity, values), nil
|
return tpl.ParseTemplateWithArgs(arg, entity, values), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateArguments(values map[string]string, action *config.Action) error {
|
func validateArguments(values map[string]string, action *config.Action) error {
|
||||||
|
|
@ -117,7 +118,7 @@ func parseActionArguments(values map[string]string, action *config.Action, entit
|
||||||
}).Debugf("Arg assigned")
|
}).Debugf("Arg assigned")
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedShellCommand := entities.ParseTemplateWithArgs(rawShellCommand, entity, values)
|
parsedShellCommand := tpl.ParseTemplateWithArgs(rawShellCommand, entity, values)
|
||||||
redactedShellCommand := redactShellCommand(parsedShellCommand, action.Arguments, values)
|
redactedShellCommand := redactShellCommand(parsedShellCommand, action.Arguments, values)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -256,7 +257,7 @@ func typecheckChoiceEntity(value string, arg *config.ActionArgument) error {
|
||||||
templateChoice := arg.Choices[0].Value
|
templateChoice := arg.Choices[0].Value
|
||||||
|
|
||||||
for _, ent := range entities.GetEntityInstances(arg.Entity) {
|
for _, ent := range entities.GetEntityInstances(arg.Entity) {
|
||||||
choice := entities.ParseTemplateWith(templateChoice, ent)
|
choice := tpl.ParseTemplateWith(templateChoice, ent)
|
||||||
|
|
||||||
if value == choice {
|
if value == choice {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
authpublic "github.com/OliveTin/OliveTin/internal/auth/authpublic"
|
authpublic "github.com/OliveTin/OliveTin/internal/auth/authpublic"
|
||||||
config "github.com/OliveTin/OliveTin/internal/config"
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
"github.com/OliveTin/OliveTin/internal/entities"
|
"github.com/OliveTin/OliveTin/internal/entities"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/tpl"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
|
@ -737,7 +738,7 @@ func stepRequestAction(req *ExecutionRequest) bool {
|
||||||
|
|
||||||
req.logEntry.Binding = req.Binding
|
req.logEntry.Binding = req.Binding
|
||||||
req.logEntry.ActionConfigTitle = req.Binding.Action.Title
|
req.logEntry.ActionConfigTitle = req.Binding.Action.Title
|
||||||
req.logEntry.ActionTitle = entities.ParseTemplateWith(req.Binding.Action.Title, req.Binding.Entity)
|
req.logEntry.ActionTitle = tpl.ParseTemplateWith(req.Binding.Action.Title, req.Binding.Entity)
|
||||||
req.logEntry.ActionIcon = req.Binding.Action.Icon
|
req.logEntry.ActionIcon = req.Binding.Action.Icon
|
||||||
req.logEntry.Tags = req.Tags
|
req.logEntry.Tags = req.Tags
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,75 @@
|
||||||
package entities
|
package tpl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/OliveTin/OliveTin/internal/entities"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/installationinfo"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var tpl = template.New("tpl")
|
var tpl = template.New("tpl")
|
||||||
|
|
||||||
|
type olivetinInfo struct {
|
||||||
|
Build *installationinfo.BuildInfo
|
||||||
|
Runtime *installationinfo.RuntimeInfo
|
||||||
|
}
|
||||||
|
|
||||||
var legacyArgumentRegex = regexp.MustCompile(`{{ ([a-zA-Z0-9_]+) }}`)
|
var legacyArgumentRegex = regexp.MustCompile(`{{ ([a-zA-Z0-9_]+) }}`)
|
||||||
var legacyEntityPropertiesRegex = regexp.MustCompile(`{{ ([a-zA-Z0-9_]+)\.([a-zA-Z0-9_\.]+) }}`)
|
var legacyEntityPropertiesRegex = regexp.MustCompile(`{{ ([a-zA-Z0-9_]+)\.([a-zA-Z0-9_\.]+) }}`)
|
||||||
|
|
||||||
|
type generalTemplateContext struct {
|
||||||
|
OliveTin olivetinInfo
|
||||||
|
Env map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type actionTemplateContext struct {
|
||||||
|
CurrentEntity interface{}
|
||||||
|
Arguments map[string]string
|
||||||
|
|
||||||
|
// These are deliberately repeated because embedding structs
|
||||||
|
// won't work in text/template.
|
||||||
|
OliveTin olivetinInfo
|
||||||
|
Env map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
cachedOliveTinInfo olivetinInfo
|
||||||
|
cachedEnvMap map[string]string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cachedOliveTinInfo = olivetinInfo{
|
||||||
|
Build: installationinfo.Build,
|
||||||
|
Runtime: installationinfo.Runtime,
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedEnvMap = buildEnvMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNewGeneralTemplateContext() *generalTemplateContext {
|
||||||
|
return &generalTemplateContext{
|
||||||
|
OliveTin: cachedOliveTinInfo,
|
||||||
|
Env: cachedEnvMap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildEnvMap() map[string]string {
|
||||||
|
envMap := make(map[string]string)
|
||||||
|
for _, env := range os.Environ() {
|
||||||
|
parts := strings.SplitN(env, "=", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
envMap[parts[0]] = parts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return envMap
|
||||||
|
}
|
||||||
|
|
||||||
func migrateLegacyEntityProperties(rawShellCommand string) string {
|
func migrateLegacyEntityProperties(rawShellCommand string) string {
|
||||||
foundArgumentNames := legacyEntityPropertiesRegex.FindAllStringSubmatch(rawShellCommand, -1)
|
foundArgumentNames := legacyEntityPropertiesRegex.FindAllStringSubmatch(rawShellCommand, -1)
|
||||||
|
|
||||||
|
|
@ -68,7 +124,7 @@ func migrateLegacyArgumentNames(rawShellCommand string) string {
|
||||||
return rawShellCommand
|
return rawShellCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseTemplateWithArgs(source string, ent *Entity, args map[string]string) string {
|
func ParseTemplateWithArgs(source string, ent *entities.Entity, args map[string]string) string {
|
||||||
source = migrateLegacyArgumentNames(source)
|
source = migrateLegacyArgumentNames(source)
|
||||||
source = migrateLegacyEntityProperties(source)
|
source = migrateLegacyEntityProperties(source)
|
||||||
|
|
||||||
|
|
@ -90,11 +146,12 @@ func ParseTemplateWithArgs(source string, ent *Entity, args map[string]string) s
|
||||||
entdata = ent.Data
|
entdata = ent.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
templateVariables := &variableBase{
|
templateVariables := &actionTemplateContext{
|
||||||
OliveTin: GetAll().OliveTin,
|
OliveTin: cachedOliveTinInfo,
|
||||||
|
Env: cachedEnvMap,
|
||||||
|
|
||||||
Arguments: args,
|
Arguments: args,
|
||||||
CurrentEntity: entdata,
|
CurrentEntity: entdata,
|
||||||
Env: GetAll().Env,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
|
|
@ -114,21 +171,14 @@ func ParseTemplateWithArgs(source string, ent *Entity, args map[string]string) s
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseTemplateWith(source string, ent *Entity) string {
|
func ParseTemplateWith(source string, ent *entities.Entity) string {
|
||||||
return ParseTemplateWithArgs(source, ent, nil)
|
return ParseTemplateWithArgs(source, ent, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseTemplateBoolWith(source string, ent *Entity) bool {
|
func ParseTemplateBoolWith(source string, ent *entities.Entity) bool {
|
||||||
source = strings.TrimSpace(source)
|
source = strings.TrimSpace(source)
|
||||||
|
|
||||||
tplBool := ParseTemplateWith(source, ent)
|
tplBool := ParseTemplateWith(source, ent)
|
||||||
|
|
||||||
return tplBool == "true"
|
return tplBool == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClearEntities(entityType string) {
|
|
||||||
rwmutex.Lock()
|
|
||||||
defer rwmutex.Unlock()
|
|
||||||
|
|
||||||
delete(contents.Entities, entityType)
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue