knadh / koanf

Simple, extremely lightweight, extensible, configuration management library for Go. Support for JSON, TOML, YAML, env, command line, file, S3 etc. Alternative to viper.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to decode hcl file data

RATANAJANGIR opened this issue · comments

I am using unmarshalwithconf to decode hcl file data. ( composing multiple decodehookFunc)
But getting error to decode []map[string]interface{} data.
Anyone can help ?

Please share the code you're trying with sample HCL data.

Please share the code you're trying with sample HCL data.

var Source = context.NewContext()

func DecodeSourceContext(box *rice.Box) error {
	var languageResolver language.Resolver
	var automationTools []management.AutomationTool
	var languages []language.Language
	sourceKoanf := koanf.New(".")
	_ = sourceKoanf.Load(rawbytes.Provider(box.MustBytes(fmt.Sprintf("structs/%s.hcl", language.ConfigurationKey))), hcl.Parser(false))
	_ = sourceKoanf.Load(rawbytes.Provider(box.MustBytes(fmt.Sprintf("structs/%s.hcl", xstrings.ToKebabCase(management.ConfigurationKey)))), hcl.Parser(false))

	if err := sourceKoanf.UnmarshalWithConf(language.ConfigurationKey, nil, koanf.UnmarshalConf{
		DecoderConfig: &mapstructure.DecoderConfig{
			DecodeHook: language.DecodeHookFunc,
			Result:     &languages,
		},
	}); err != nil {
		logrus.Fatal("Failure to unmarshall source project languages", err)
	} else {
		languageResolver = language.NewResolver(languages...)
		Source.SetLanguageResolver(languageResolver)
	}
	if err := sourceKoanf.UnmarshalWithConf(management.ConfigurationKey, nil, koanf.UnmarshalConf{
		DecoderConfig: &mapstructure.DecoderConfig{
			DecodeHook: management.AutomationToolDecodeHookFuncProvider(languageResolver),
			Result:     &automationTools,
		},
	}); err != nil {
		logrus.Fatal("Failure to unmarshall source project automation tool", err)
	} else {
		Source.LoadAutomationTool(automationTools...)
	}
	return nil
}

#########################################

type Language interface {
	LanguageId() string
	LanguageName() string
	DecodeE(map[string]interface{}) error
}

func DecodeHookFunc(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) {
	var err error
	if from.Kind() == reflect.Map && to.Implements(reflect.TypeOf((*Language)(nil)).Elem()) {
		dataMap := cast.ToStringMap(data)
		var language Language
		languageType := strings.ToLower(cast.ToString(dataMap["type"]))
		switch languageType {
		case "programmable":
			language = new(ProgrammableLanguage)
		case "executable":
			language = new(ExecutableLanguage)
		default:
			err = fmt.Errorf("Failure to decode Language due to invalid type[:= %s]", languageType)
		}
		if language != nil {
			err = language.DecodeE(dataMap)
		}
		return language, err
	}
	return data, err
}

I am trying to upgrade api version in my code from v0.15.0 to v1.4.4
This function working fine when i am using v0.15.0 version of koanf api but in v0.16.0 its failing and above this version also.

getting error of "Failure to unmarshall source project languages 3".

HCL file data

HCL file data

language {
  id = "go"
  name = "GoLang"
  command = "go"
  specification = {
    environment = {
      builder = {
        Registry = {
          Address = "https...."
        }
        user = "doe/chapters/devops-engineering/"
        app = "go-builder"
      }
      runtime = {
        app = "scratch"
        platformless = true
      }
    }
    templates = {
      artifact-path = "{{ config \"module.app\" }}"
      start-command = "./{{ config \"module.app\" }}"
      component-install = "go get {{.}}"
    }
  }
  type = "executable"
}

ERROR 1
error decoding 'Specification': unsupported entry[type= []map[string]interface
{}, value= [map[environment:[map[builder:[map[Registry:[map[Address:https....]] app:go-builder user:doe/chapters/devops-engineering/]] runtime
:[map[app:scratch platformless:true]]]] templates:[map[artifact-path:{{ config "
module.app" }} component-install:go get {{.}} start-command:./{{ config "module.
app" }}]]]]] when attempting to decode Specification definition

type ExecutableLanguage struct {
	ProgrammableLanguage `mapstructure:",squash"`
	Command              string
	Specification        *build.Specification
}

func (el *ExecutableLanguage) DecodeE(data map[string]interface{}) error {
	if elDecoder, initErr := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		DecodeHook: mapstructure.ComposeDecodeHookFunc(decode.HCLMapstructureHookFunc, build.SpecificationDecodeHookFunc),
		Result:     el,
	}); initErr != nil {
		return errors.Wrap(initErr, "Failure to initialize ExecutableLanguage mapstructure.Decoder")
	} else {
		return errors.Wrap(elDecoder.Decode(data), "Failure to decode ExecutableLanguage structure")
	}
}



type (
	Specification struct {
		Environment
		Templates
	}

	Environment struct {
		Build   image.Image `mapstructure:"builder"`
		Runtime image.Image
	}

	Templates struct {
		ArtifactPathTemplate          string                      `mapstructure:"artifact-path"`
		StartCommandTemplate          *executable.TemplateCommand `mapstructure:"start-command"`
		ComponentInstallationTemplate *executable.TemplateCommand `mapstructure:"component-install"`
		ExecutableBinary              string                      `mapstructure:"executable-binary"`
	}
)

getting error -

  • error decoding '[1]': Failure to decode ExecutableLanguage structure: 1 error(s) decoding:

  • error decoding 'Specification': unsupported entry[type= []map[string]interface {}, value= [map[environment:[map[builder:[map[Registry:[map[Address:https....]] app:go-builder user:doe/chapters/devops-engineering/]] runtime:[map[app:scratch platformless:true]]]] templates:[map[artifact-path:{{ config "module
    .app" }} component-install:go get {{.}} start-command:./{{ config "module.app" }}]]]]] when attempting to decode Specification definition

I really need help to resolve this issue

Looks like you have custom types such as *executable.TemplateCommand which will not work with unmarshalling.

Please edit the code sample in your post with backticks so that they're formatted and readable.

func TestSpecificationDecodeHookFunc(t *testing.T) {
	if testSpecification, err := SpecificationDecodeHookFunc(reflect.TypeOf((map[string]interface{})(nil)), reflect.TypeOf((*Specification)(nil)).Elem(), map[string]interface{}{
		"environment": map[string]interface{}{
			"builder": map[string]interface{}{
				"registry": map[string]interface{}{"address": "test-git.xyzcorp.com"},
				"user":     "devops",
				"app":      "go-builder",
			},
			"runtime": map[string]interface{}{
				"app": "scratch",
			},
		},
		"templates": map[string]interface{}{
			"artifact-path":     "./path/test",
			"start-command":     "{{ config \"module.app\" }}",
			"component-install": "go get {{ .Settings.Get \"component\" }}",
		},
	}); assert.Nil(t, err, "Specification threw faulty error message when attempting to decode struct from valid mapstructure input") &&
		assert.Equal(t, &Specification{
			Environment: Environment{
				Build:   image.Builder().RegistryAddress("test-git.xyzcorp.com").User("devops").App("go-builder").Build(),
				Runtime: image.Builder().App("scratch").Build(),
			},
			Templates: Templates{
				ArtifactPathTemplate:          "./path/test",
				StartCommandTemplate:          &executable.TemplateCommand{"{{ config \"module.app\" }}"},
				ComponentInstallationTemplate: &executable.TemplateCommand{"go get {{ .Settings.Get \"component\" }}"},
			},
		}, testSpecification, "Failure to correctly decode Specification struct from valid mapstructure entry") {
		t.Log("Successfully decoded Specification from valid mapstructure entry")
	}
}

This test case is working file. Can you check now ?

Sorry, I am unable to figure out what's going on. This test case has nothing to do with koanf or mapstructure unmarshalling, right?

error decoding 'Specification': unsupported entry[type= []map[string]interface {}

The error clearly says that custom type Specification isn't supported. I'm not sure how the mapstructure lib handles custom types, but this could be a starting point: mitchellh/mapstructure#115

encoding Text(Un)marshaller needs to bevimplemented for custom types.