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

Valueless keys #12

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Valueless keys #12

wants to merge 5 commits into from

Conversation

khuey
Copy link
Contributor

@khuey khuey commented Dec 21, 2017

In environment sections you can specify valueless keys like

environment:
  - FOO
  - BAR

And docker-compose will pick up the relevant values on the machine compose runs on.

The other form,

environment:
  FOO:
  BAR:

would require serde_yaml changes, I believe.

@dtolnay
Copy link

dtolnay commented Dec 21, 2017

Serde_yaml sees the second form as having null values. This matches other YAML libraries I tried.

extern crate serde_yaml;
use serde_yaml::Value;

const Y: &str = "
---
environment:
  FOO:
  BAR:
";

fn main() {
    println!("{:#?}", serde_yaml::from_str::<Value>(Y).unwrap());
}

Copy link
Owner

@emk emk left a comment

Choose a reason for hiding this comment

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

Thank you for this patch! I think I recall seeing this in the docker-compose.yml standard, and I would love to have a compliant implementation of this part of the standard.

compose_yml currently supports the docker-compose.yml specifications for versions "2" and "2.1" (and we could consider adding support for later "2.x" versions pretty easily, though "3.x" would probably be harder). In particular, we want to make sure that we don't support any non-compliant extensions and that we conform strictly to the standard.

So in order to help us remain compliant, could you please point me to where this behavior is specified in the docker-compose.yml specs? This will make it easier for code reviewers. (And we need to be careful, given that schema validation is currently broken because of s-panferov/valico#27).

Also, this PR has no test cases. Could you please look at the various assert_roundtrip! test cases in the code base, and make sure that there are test cases for this feature, showing that it handles (1) all the old behaviors, and (2) the new behavior? Thank you!

To do items:

  • A link to the relevant part of the standard for version "2" or "2.1".
  • Test cases.
  • Other issues mentioned below.

Thank you for this PR!

new_env.append(&mut env_file.to_environment()?);
new_env.extend(env_file.to_environment()?
.into_iter()
.map(|(k, v)| (k, Some(v))));
Copy link
Owner

@emk emk Dec 21, 2017

Choose a reason for hiding this comment

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

If environments now have type BTreeMap<String, Option<RawOr<String>>>, then we should consider updating to_environment to also return that type, instead of patching it up here. Or is something more complicated going on that we need to think about first?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Turns out env_files also accept valueless keys, so we should just handle them there and produce the same BTreeMap type.

}
}

deserializer.deserialize_map(MapOrKeyOptionalValueListVisitor)
Copy link
Owner

Choose a reason for hiding this comment

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

Just last night, I discovered in serde-rs/serde#1125 (comment) that there has been a breaking change to the serde crate, and it is no longer safe to call deserialize_map as a hint when when override both visit_map and visit_seq. This needs to be deserialize_any (and I need to fix this everywhere else in compose_yml as well). Sorry for this last minute surprise. :-(

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I fixed this, and in the other obvious places too.

@@ -95,7 +95,7 @@ impl<'de> Deserialize<'de> for ConvertToString {
///
/// ```text
/// struct Example {
/// #[serde(deserialize_with = "deserialize_hash_or_key_value_list")]
/// #[serde(deserialize_with = "deserialize_map_or_key_value_list")]
Copy link
Owner

Choose a reason for hiding this comment

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

Good catch!

@@ -72,8 +72,8 @@ pub struct Service {

/// Environment variables and values to supply to the container.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty",
deserialize_with = "deserialize_map_or_key_value_list")]
pub environment: BTreeMap<String, RawOr<String>>,
deserialize_with = "deserialize_map_or_key_optional_value_list")]
Copy link
Owner

Choose a reason for hiding this comment

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

Have we looked at all the other callers of deserialize_map_or_key_value_list and verified that none of them require similar conversions? I can check this if you want, but it will probably have to wait until sometime next week at least.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tested all of them with docker_compose and the only one that doesn't accept valueless keys is driver_opts. All the others do.

This raises some mildly interesting questions in cage, such as what to do if io.fdy.cage.srcdir is set but null.

@khuey
Copy link
Contributor Author

khuey commented Dec 21, 2017

I don't know if there's a spec for docker-compose.yml beyond the reference, but it's documented at https://docs.docker.com/compose/compose-file/compose-file-v2/#environment with no minor version qualifications.

The reason I didn't add an assert_roundtrip! test is that this already fails even for keys with values because compose_yml canonicalizes the array form to the object form. i.e.

environment:
  - FOO=bar

fails an assert_roundtrip! with

  left: `Object({"environment": Object({"FOO": String("bar")})})`,
 right: `Object({"environment": Array([String("FOO=bar")])})`'

docker-compose doesn't seem to document this behavior but it does accept these.
Through testing, only the `driver_opts` subkey of `volumes` actually rejects missing values.
Everything else that uses `deserialize_map_or_key_value_list` does accept them.
@khuey
Copy link
Contributor Author

khuey commented Jan 2, 2018

What else needs to happen here?

@@ -79,6 +82,7 @@ fn parses_docker_compatible_env_files() {
# These are environment variables:
FOO=foo
BAR=2
BAZ
Copy link
Owner

Choose a reason for hiding this comment

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

As far as I can tell from the documentation, this is a non-standard extension to the .env format:

Compose expects each line in an env file to be in VAR=VAL format. Lines beginning with # (i.e. comments) are ignored, as are blank lines.

So unless we can find some evidence that Docker supports this extension, we should remove this code.

Copy link
Owner

@emk emk left a comment

Choose a reason for hiding this comment

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

Thank you for the update!

We need to either justify or remove the support for empty keys in .env.

"value");
assert_eq!(*build.args.get("other_key").expect("should have other_key"), None);
Copy link
Owner

Choose a reason for hiding this comment

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

OK, good, this is the main test that we need.

@khuey
Copy link
Contributor Author

khuey commented Jan 2, 2018

I don't see it documented anywhere but it does happen.

khuey@workspace:~/scratch/docker-test$ docker -v
Docker version 1.13.1, build 092cba3
khuey@workspace:~/scratch/docker-test$ docker-compose -v
docker-compose version 1.18.0, build 8dd22a9
khuey@workspace:~/scratch/docker-test$ cat docker-compose.yml
test:
  image: jwilder/whoami
  env_file: test.env
khuey@workspace:~/scratch/docker-test$ cat test.env
TEST_VALUE
khuey@workspace:~/scratch/docker-test$ export TEST_VALUE=Hi
khuey@workspace:~/scratch/docker-test$ docker-compose up -d
Recreating dockertest_test_1 ... done
khuey@workspace:~/scratch/docker-test$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
56dfcff90ec5        jwilder/whoami      "/app/http"         3 seconds ago       Up 2 seconds        8000/tcp            dockertest_test_1
khuey@workspace:~/scratch/docker-test$ docker exec -it 56df sh
/app # echo $TEST_VALUE
Hi

@khuey
Copy link
Contributor Author

khuey commented Jan 2, 2018

(docker-compose behaves this way with an explicit version: "2" in the compose file as well)

@emk
Copy link
Owner

emk commented Jan 2, 2018

@khuey OK, if docker-compose supports it, we can certainly implement it.

But please keep in mind my comments on faradayio/cage#87 (comment). We don't have any kind of coherent, principled model for how cage should handle env interpolation, so I'd be careful about relying on these features too heavily if you're using them with cage—at least until we have solid conceptual model about how they should work.

Anyway, this looks about ready to merge. If you don't hear from me by Wednesday, please ping me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants