Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to decode hcl file data #184

Closed
RATANAJANGIR opened this issue Dec 16, 2022 · 11 comments
Closed

Unable to decode hcl file data #184

RATANAJANGIR opened this issue Dec 16, 2022 · 11 comments
Labels
question Further information is requested

Comments

@RATANAJANGIR
Copy link

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

@RATANAJANGIR RATANAJANGIR added the bug Something isn't working label Dec 16, 2022
@knadh
Copy link
Owner

knadh commented Dec 17, 2022

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

@knadh knadh added question Further information is requested and removed bug Something isn't working labels Dec 17, 2022
@RATANAJANGIR
Copy link
Author

RATANAJANGIR commented Dec 19, 2022

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

@RATANAJANGIR
Copy link
Author

RATANAJANGIR commented Dec 19, 2022

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"
}

@RATANAJANGIR
Copy link
Author

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

@RATANAJANGIR
Copy link
Author

RATANAJANGIR commented Dec 20, 2022

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

@RATANAJANGIR
Copy link
Author

I really need help to resolve this issue

@knadh
Copy link
Owner

knadh commented Dec 20, 2022

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

@knadh
Copy link
Owner

knadh commented Dec 20, 2022

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

@RATANAJANGIR
Copy link
Author

RATANAJANGIR commented Dec 20, 2022

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 ?

@knadh
Copy link
Owner

knadh commented Dec 20, 2022

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

@jxsl13
Copy link
Contributor

jxsl13 commented Feb 4, 2023

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

@knadh knadh closed this as completed Feb 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants