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

Generated client for ExternalIpCreate feels clunky #1501

Closed
iliana opened this issue Jul 26, 2022 · 6 comments · Fixed by #1502
Closed

Generated client for ExternalIpCreate feels clunky #1501

iliana opened this issue Jul 26, 2022 · 6 comments · Fixed by #1502

Comments

@iliana
Copy link
Contributor

iliana commented Jul 26, 2022

image
image

type_ is awkward here. What I would expect is to be able to write something like ExternalIpCreate::Ephemeral { pool_name: None }; is the pool name relevant for all future kinds of external IP creation objects?

If we want to leave the shape the same I would recommend renaming type to kind.

cc @bnaecker

@bnaecker
Copy link
Collaborator

bnaecker commented Jul 27, 2022

I think this is due to interaction with the generator, either because we're using an externally-tagged type or how the generator consumes them. The native type definition is indeed what you were hoping to write. The tag-name type is the most common choice in Omicron today (though not the only one), and I think renaming it to kind is perfectly fine.

Also, I recall some discussion a while ago about generating enum representations in a way that feels at least passable in a number of languages. All I can find at the moment is #573, but @ahl might have a better memory for that.

@ahl
Copy link
Contributor

ahl commented Jul 27, 2022

Progenitor usually does a good job of reconstructing enums. This is a case I hadn't considered: an enum with a single variant. Here's what the OpenAPI JSON looks like:

      "ExternalIpCreate": {
        "description": "Parameters for creating an external IP address for instances.",
        "oneOf": [
          {
            "description": "An IP address providing both inbound and outbound access. The address is automatically-assigned from the provided IP Pool, or all available pools if not specified.",
            "type": "object",
            "properties": {
              "pool_name": {
                "nullable": true,
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Name"
                  }
                ]
              },
              "type": {
                "type": "string",
                "enum": [
                  "ephemeral"
                ]
              }
            },
            "required": [
              "type"
            ]
          }
        ]
      },

For quite awhile, typify has made the assumption that a lone subschema can just be treated as a wrapper around the subordinate schema. This is fairly common construction... we even see it above with the $ref for Name!

We can fix this by first seeing if the type fits one of our more attractive enum types (i.e. not untagged) and only failing that, seeing if there's a singleton subschema.

Note however that these two enums emit the same JSON schema:

#[derive(JsonSchema)]
#[serde(tag = "type")] // internal tagging
pub enum ExternalIpCreate {
    Ephemeral { pool_name: Option<Name> },
}

#[derive(JsonSchema)]
#[serde(tag = "type", content = "pool_name")] // adjacent tagging
pub enum ExternalIpCreate {
    Ephemeral(String),
}

Typify needs to prefer one or the other when interpreting the JSON schema, and there's a clear choice because adjacent tagging is a strict subset of internal tagging! Consider this enum:

#[derive(JsonSchema)]
#[serde(tag = "who", content = "what")]
pub enum Friend {
    Bert(String),
    Ernie(String),
}

If we were to prefer to interpret this with internal tagging, we'd reconstruct this:

#[derive(JsonSchema)]
#[serde(tag = "who")]
    Bert { what: String },
    Ernie { what: String },
}

... which would be gross.

This fix for this needs to go into typify and then percolate to progenitor and then omicron.

@ahl
Copy link
Contributor

ahl commented Jul 27, 2022

Here's the original:

/// Parameters for creating an external IP address for instances.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ExternalIpCreate {
    /// An IP address providing both inbound and outbound access. The address is
    /// automatically-assigned from the provided IP Pool, or all available pools
    /// if not specified.
    Ephemeral { pool_name: Option<Name> },
    // TODO: Add floating IPs: https://github.com/oxidecomputer/omicron/issues/1334
}

With the fixes applied, here's how it comes out:

    ///Parameters for creating an external IP address for instances.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    #[serde(tag = "type")]
    pub enum ExternalIpCreate {
        ///An IP address providing both inbound and outbound access. The
        /// address is automatically-assigned from the provided IP Pool, or all
        /// available pools if not specified.
        #[serde(rename = "ephemeral")]
        Ephemeral {
            #[serde(default, skip_serializing_if = "Option::is_none")]
            pool_name: Option<Name>,
        },
    }

Everyone happy?

@bnaecker
Copy link
Collaborator

bnaecker commented Jul 27, 2022 via email

@ahl
Copy link
Contributor

ahl commented Jul 27, 2022

Meh... turns out Digest had this problem too:

before

    pub struct Digest {
        #[serde(rename = "type")]
        pub type_: DigestType,
        pub value: String,
    }

    #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
    pub enum DigestType {
        #[serde(rename = "sha256")]
        Sha256,
    }

after

    #[serde(tag = "type", content = "value")]
    pub enum Digest {
        #[serde(rename = "sha256")]
        Sha256(String),
    }

@bnaecker
Copy link
Collaborator

Fair enough! Glad it was neither a lot of work, nor a one-off for this type. It seems like we'll resolve this with the merger of #1502, correct? Thanks Adam.

@iliana iliana linked a pull request Jul 27, 2022 that will close this issue
@ahl ahl closed this as completed in #1502 Jul 27, 2022
leftwo pushed a commit that referenced this issue Oct 25, 2024
Crucible changes
Add test and fix for replay race condition (#1519)
Fix clippy warnings (#1517)
Add edition to `crucible-workspace-hack` (#1516)
Split out Downstairs-specific stats (#1511)
Move remaining `GuestWork` functionality into `Downstairs` (#1510)
Track both jobs and bytes in each IO state (#1507)
Fix new `rustc` and `clippy` warnings (#1509)
Remove IOP/BW limits (for now) (#1506)
Move `GuestBlockRes` into `DownstairsIO` (#1502)
Update actions/checkout digest to eef6144 (#1499)
Update Rust crate hyper-staticfile to 0.10.1 (#1411)
Turn off test-up-2region-encrypted.sh (#1504)
Add `IOop::Barrier` (#1494)
Fix IPv6 addresses in `crutest` (#1503)
Add region set options to more tests. (#1496)
Simplify `CompleteJobs` (#1493)
Removed ignored CI jobs (#1497)
Minor cleanups to `print_last_completed` (#1501)
Remove remaining `Arc<Volume>` instances (#1500)
Add `VolumeBuilder` type (#1492)
remove old unused scripts (#1495)
More multiple region support. (#1484)
Simplify matches (#1490)
Move complete job tracker to a helper object (#1489)
Expand summary and add documentation references to the README. (#1486)
Remove `GuestWorkId` (2/2) (#1482)
Remove `JobId` from `DownstairsIO` (1/2) (#1481)
Remove unused `#[derive(..)]` (#1483)
Update more tests to use dsc (#1480)
Crutest now Volume only (#1479)

Propolis changes
manually impl Deserialize for PciPath for validation purposes (#801)
phd: gate OS-specific tests, make others more OS-agnostic (#799)
lib: log vCPU diagnostics on triple fault and for some unhandled exit types (#795)
add marker trait to help check safety of guest memory reads (#794)
clippy fixes for 1.82 (#796)
lib: move cpuid::Set to cpuid_utils; prevent semantic subleaf conflicts (#782)
PHD: write efivars in one go (#786)
PHD: support guest-initiated reboot (#785)
server: accept CPUID values in instance specs and plumb them to bhyve (#780)
PHD: allow patched Crucible dependencies (#778)
server: add a first-class error type to machine init (#777)
PciPath to Bdf conversion is infallible; prove it and refactor (#774)
instance spec rework: flatten InstanceSpecV0 (#767)
Make PUT /instance/state 503 when waiting to init
Less anxiety-inducing `Vm::{get, state_watcher}`
leftwo added a commit that referenced this issue Oct 30, 2024
Crucible changes
Add test and fix for replay race condition (#1519) Fix clippy warnings
(#1517)
Add edition to `crucible-workspace-hack` (#1516)
Split out Downstairs-specific stats (#1511)
Move remaining `GuestWork` functionality into `Downstairs` (#1510) Track
both jobs and bytes in each IO state (#1507) Fix new `rustc` and
`clippy` warnings (#1509)
Remove IOP/BW limits (for now) (#1506)
Move `GuestBlockRes` into `DownstairsIO` (#1502)
Update actions/checkout digest to eef6144 (#1499)
Update Rust crate hyper-staticfile to 0.10.1 (#1411) Turn off
test-up-2region-encrypted.sh (#1504)
Add `IOop::Barrier` (#1494)
Fix IPv6 addresses in `crutest` (#1503)
Add region set options to more tests. (#1496)
Simplify `CompleteJobs` (#1493)
Removed ignored CI jobs (#1497)
Minor cleanups to `print_last_completed` (#1501)
Remove remaining `Arc<Volume>` instances (#1500)
Add `VolumeBuilder` type (#1492)
remove old unused scripts (#1495)
More multiple region support. (#1484)
Simplify matches (#1490)
Move complete job tracker to a helper object (#1489) Expand summary and
add documentation references to the README. (#1486) Remove `GuestWorkId`
(2/2) (#1482)
Remove `JobId` from `DownstairsIO` (1/2) (#1481)
Remove unused `#[derive(..)]` (#1483)
Update more tests to use dsc (#1480)
Crutest now Volume only (#1479)

Propolis changes
manually impl Deserialize for PciPath for validation purposes (#801)
phd: gate OS-specific tests, make others more OS-agnostic (#799) lib:
log vCPU diagnostics on triple fault and for some unhandled exit types
(#795) add marker trait to help check safety of guest memory reads
(#794) clippy fixes for 1.82 (#796)
lib: move cpuid::Set to cpuid_utils; prevent semantic subleaf conflicts
(#782) PHD: write efivars in one go (#786)
PHD: support guest-initiated reboot (#785)
server: accept CPUID values in instance specs and plumb them to bhyve
(#780) PHD: allow patched Crucible dependencies (#778)
server: add a first-class error type to machine init (#777) PciPath to
Bdf conversion is infallible; prove it and refactor (#774) instance spec
rework: flatten InstanceSpecV0 (#767) Make PUT /instance/state 503 when
waiting to init
Less anxiety-inducing `Vm::{get, state_watcher}`

---------

Co-authored-by: Alan Hanson <alan@oxide.computer>
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 a pull request may close this issue.

3 participants