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

Added ACTIVESTATE_CLI_IGNORE_ENV for ignoring env vars during runtime setup. #3493

Merged
merged 4 commits into from
Sep 23, 2024

Conversation

mitchell-as
Copy link
Contributor

@mitchell-as mitchell-as commented Sep 17, 2024

TaskDX-3011 Support overriding PYTHONPATH

For example, PYTHONPATH.

@mitchell-as mitchell-as changed the base branch from master to version/0-47-0-RC1 September 17, 2024 16:32
@mitchell-as mitchell-as marked this pull request as ready for review September 17, 2024 17:01
Comment on lines 162 to 168
if ignores := os.Getenv(constants.IgnoreEnvEnvVarName); ignores != "" {
for _, name := range strings.Split(ignores, ",") {
if _, exists := vars[name]; exists {
delete(vars, name)
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is deleting environment variables regardless of whether we created them. If inherit is true then you might be deleting the very PYTHONPATH the user was trying to set, along with ours.

I think we need to address this inside the envdef package. And it should prevent defining the env var, rather than delete it after the fact (unless we can do it without inheritance playing a role).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, there's an integration test that verifies we're leaving the user's set PYTHONPATH alone. This deletion only affects the runtime environment, which has not yet been merged with the current one. Since this runtime environment does not contain PYTHONPATH, when merging with the current one, the user's set PYTHONPATH will remain.

I tried to make this happen in envdef, but there are at least three different places where environment mutations happen, and all would need the check:

  • func (ed *EnvironmentDefinition) Merge(other *EnvironmentDefinition) (*EnvironmentDefinition, error) {
    res := *ed
    if other == nil {
    return &res, nil
    }
    newEnv := []EnvironmentVariable{}
    thisEnvNames := funk.Map(
    ed.Env,
    func(x EnvironmentVariable) string { return x.Name },
    ).([]string)
    newKeys := make([]string, 0, len(other.Env))
    otherEnvMap := map[string]EnvironmentVariable{}
    for _, ev := range other.Env {
    if !funk.ContainsString(thisEnvNames, ev.Name) {
    newKeys = append(newKeys, ev.Name)
    }
    otherEnvMap[ev.Name] = ev
    }
    // add new keys to environment
    for _, k := range newKeys {
    oev := otherEnvMap[k]
    newEnv = append(newEnv, oev)
    }
    // merge keys
    for _, ev := range ed.Env {
    otherEv, ok := otherEnvMap[ev.Name]
    if !ok {
    // if key exists only in this variable, use it
    newEnv = append(newEnv, ev)
    } else {
    // otherwise: merge this variable and the other environment variable
    mev, err := ev.Merge(otherEv)
    if err != nil {
    return &res, err
    }
    newEnv = append(newEnv, *mev)
    }
    }
    res.Env = newEnv
    return &res, nil
  • func (ed *EnvironmentDefinition) GetEnv(inherit bool) map[string]string {
    lookupEnv := map[string]string{}
    if inherit {
    lookupEnv = osutils.EnvSliceToMap(os.Environ())
    }
    res, err := ed.GetEnvBasedOn(lookupEnv)
    if err != nil {
    panic(fmt.Sprintf("Could not inherit OS environment variable: %v", err))
    }
    return res
  • func (ed *EnvironmentDefinition) GetEnvBasedOn(envLookup map[string]string) (map[string]string, error) {
    res := maps.Clone(envLookup)
    for _, ev := range ed.Env {
    pev := &ev
    if pev.Inherit {
    osValue, hasOsValue := envLookup[pev.Name]
    if hasOsValue {
    osEv := ev
    osEv.Values = []string{osValue}
    var err error
    pev, err = osEv.Merge(ev)
    if err != nil {
    return nil, err
    }
    }
    } else if _, hasOsValue := envLookup[pev.Name]; hasOsValue {
    res[pev.Name] = "" // unset
    }
    // only add environment variable if at least one value is set (This allows us to remove variables from the environment.)
    if len(ev.Values) > 0 {
    res[pev.Name] = pev.ValueString()
    }
    }
    return res, nil

I thought it would be better to capture it at the top-level than all those low-level places where things can slip through the cracks if we're not careful.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree having a central place for it would be ideal, but this still feels too error prone to me. The fact that it passes in your tests worries me more than that it settles my concern, because reading through this code suggests that it should not work.

If we are to place the logic here then there needs to be an understanding of why that test is passing, and the code should clearly communicate either via logic or comments that this is working with an awareness of inheritance.

Comment on lines 162 to 168
if ignores := os.Getenv(constants.IgnoreEnvEnvVarName); ignores != "" {
for _, name := range strings.Split(ignores, ",") {
if _, exists := vars[name]; exists {
delete(vars, name)
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree having a central place for it would be ideal, but this still feels too error prone to me. The fact that it passes in your tests worries me more than that it settles my concern, because reading through this code suggests that it should not work.

If we are to place the logic here then there needs to be an understanding of why that test is passing, and the code should clearly communicate either via logic or comments that this is working with an awareness of inheritance.

@mitchell-as
Copy link
Contributor Author

Okay, I had the realization I could try to cut of envdef loading at the source, and that appears to work. Perhaps this will be okay?

Comment on lines 142 to 143
ed.Env[i] = ed.Env[len(ed.Env)-1]
ed.Env = ed.Env[:len(ed.Env)-1]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Order doesn't matter in envdefs, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe so, but this doesn't seem to change the order?

Note sliceutils.Filter() would be good here (easier to digest).

Comment on lines 142 to 143
ed.Env[i] = ed.Env[len(ed.Env)-1]
ed.Env = ed.Env[:len(ed.Env)-1]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe so, but this doesn't seem to change the order?

Note sliceutils.Filter() would be good here (easier to digest).

Comment on lines 137 to 139
for i := 0; i < len(ed.Env); {
if _, exists := ignore[ed.Env[i].Name]; !exists {
i++
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the first entry matches it seems like you would have an infinite loop here, because i will forever remain 0.

@mitchell-as mitchell-as merged commit 5515ef4 into version/0-47-0-RC1 Sep 23, 2024
8 checks passed
@mitchell-as mitchell-as deleted the mitchell/dx-3011 branch September 23, 2024 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants