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

Remove iso8601 dependency #1065

Closed
jku opened this issue Jul 3, 2020 · 12 comments · Fixed by #1176
Closed

Remove iso8601 dependency #1065

jku opened this issue Jul 3, 2020 · 12 comments · Fixed by #1176
Labels
client Related to the client (updater) implementation enhancement repository Related to the repository implementation
Milestone

Comments

@jku
Copy link
Member

jku commented Jul 3, 2020

I've been going through the tuf dependency chain with an eye on integrating tuf with pip: The issue with pip is that it's a package manager so needs to vendor everything it needs -- so being conservative with dependencies is a good idea. The good news is that tuf does not have many direct or indirect dependencies that would be a problem (good work!).

The one that possibly sticks out is "iso8601". The module is currently used in two places:

  • repository_tool: Metadata.expiration property getter uses it to return a datetime object
  • updater.py: Updater::_ensure_not_expired()

I'm mostly interested in that last one. it's used to compare the expiration stamp to current time and to format the error message:

    expires_datetime = iso8601.parse_date(expires)
    expires_timestamp = tuf.formats.datetime_to_unix_timestamp(expires_datetime)

    if expires_timestamp < current_time:
      message = 'Metadata '+repr(metadata_rolename)+' expired on ' + \
        expires_datetime.ctime() + ' (UTC).'
      logger.error(message)

      raise tuf.exceptions.ExpiredMetadataError(message)

I'm not familiar with date handling in python so my question is: Is this dependency valid or could this code be replaced with something that did not depend on iso8601?

@jku
Copy link
Member Author

jku commented Jul 3, 2020

The spec says

Metadata date-time data follows the ISO 8601 standard. The expected format of the combined date and time string is "YYYY-MM-DDTHH:MM:SSZ". Time is always in UTC, and the "Z" time zone designator is attached to indicate a zero UTC offset.

My naive understanding is that because of the zero offset requirement this might be easy to parse with standard library functions.

@jku
Copy link
Member Author

jku commented Jul 3, 2020

That said, now that I've looked at iso8601 module itself... It's tiny, literally 215 lines of code. So vendoring it might not be a problem.

@joshuagl
Copy link
Member

joshuagl commented Jul 3, 2020

#1060 makes use of dateutil. A quick browse of their docs made me think we could probably replace iso8601 with functionality in dateutil.

@joshuagl
Copy link
Member

joshuagl commented Jul 3, 2020

The spec says

Metadata date-time data follows the ISO 8601 standard. The expected format of the combined date and time string is "YYYY-MM-DDTHH:MM:SSZ". Time is always in UTC, and the "Z" time zone designator is attached to indicate a zero UTC offset.

My naive understanding is that because of the zero offset requirement this might be easy to parse with standard library functions.

oh, that would be even better!

@joshuagl joshuagl added enhancement good first issue Bite-sized items for first time contributors up for grabs labels Jul 3, 2020
@jku
Copy link
Member Author

jku commented Jul 3, 2020

#1060 makes use of dateutil. A quick browse of their docs made me think we could probably replace iso8601 with functionality in dateutil.

For the pip use case dateutil would be worse than iso8601 as it's much larger -- but I guess #1060 wouldn't necessarily mean updater changes?

Oh and for reference: securesystemslib does use dateutil but AFAICS only in one place in gpg code

@joshuagl
Copy link
Member

joshuagl commented Jul 3, 2020

#1060 makes use of dateutil. A quick browse of their docs made me think we could probably replace iso8601 with functionality in dateutil.

For the pip use case dateutil would be worse than iso8601 as it's much larger -- but I guess #1060 wouldn't necessarily mean updater changes?

In the short term, no. However, my current thought process is that the work in #1060 could become a low-level abstraction around TUF metadata that we build client/updater and repository APIs on top of. Therefore I'd like to make/keep those APIs as minimal (with as minimal dependencies) as possible so that they can be comfortably vendored into pip.

Note to self (or whoever picks up this issue): look at the use of dateutil in tuf/api and figure out if we can achieve the same with the standard library. Specifically in tuf/api/metadata we're currently using dateutil.relativedelta to cleanly specify a number of days/months/years by which to bump metadata expiration.

Oh and for reference: securesystemslib does use dateutil but AFAICS only in one place in gpg code

💯 Thanks for looking into this.

@joshuagl
Copy link
Member

The dateutil usage in securesystemslib does not appear to be necessary, so I created a PR to remove it secure-systems-lab/securesystemslib#268

@joshuagl joshuagl removed this from the Refactor milestone Sep 8, 2020
@joshuagl joshuagl removed good first issue Bite-sized items for first time contributors up for grabs labels Sep 10, 2020
@joshuagl joshuagl added this to the Foundations milestone Sep 10, 2020
@joshuagl joshuagl added client Related to the client (updater) implementation repository Related to the repository implementation labels Sep 10, 2020
@joshuagl joshuagl changed the title Updater: review iso8601 dependency Review iso8601 dependency Sep 10, 2020
@joshuagl
Copy link
Member

Our new metadata model (#1112) avoids using iso8601, which we'll build upon for both the client and repository code during the course of our refactor

@lukpueh
Copy link
Member

lukpueh commented Sep 10, 2020

@joshuagl suggests to create a helper for the iso8601 replacing call

datetime.strptime(exires, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=None)

lukpueh added a commit to lukpueh/tuf that referenced this issue Sep 10, 2020
Use builtin datetime instead of external iso6801 for simple
datetime string parsing. Also see
theupdateframework#1065

Signed-off-by: Lukas Puehringer <[email protected]>
MVrachev pushed a commit to MVrachev/tuf that referenced this issue Sep 17, 2020
Use builtin datetime instead of external iso6801 for simple
datetime string parsing. Also see
theupdateframework#1065

Signed-off-by: Lukas Puehringer <[email protected]>
@jku
Copy link
Member Author

jku commented Oct 13, 2020

I've just reviewed the iso8601 test suite and have come to the conclusion that using the iso8601 module is in fact not compliant with the (spirit of the) tuf spec: current implementation allows for timestamps that I would not expect to work after reading the spec, such as "2006-10-20T15:34:56.123+02:30".

The spec currently says:

Metadata date-time data follows the ISO 8601 standard. The expected format of the combined date and time string is "YYYY-MM-DDTHH:MM:SSZ". Time is always in UTC, and the "Z" time zone designator is attached to indicate a zero UTC offset. An example date-time string is "1985-10-21T01:21:00Z".

I think the paragraph is fairly clear but first sentence could be better: we want to support only this very specific format of the ISO8601 not the whole iso8601 spec.

As for the implementation, I think the helper idea is good. I don't think the replace() call in the example is needed: the strptime format used does not allow for a timezone at all.

@jku
Copy link
Member Author

jku commented Oct 13, 2020

Adding one more thing: the Securesystemslib schema for "iso8601 datetime string" is very strict: datetime can definitely parse that.

I think the review is over... but since there's good discussion here I'm not closing: I'll just rename this to "Remove iso8601 dependency"

@jku jku changed the title Review iso8601 dependency Remove iso8601 dependency Oct 13, 2020
jku pushed a commit to jku/python-tuf that referenced this issue Oct 13, 2020
Our 'expires' strings are constrained by the ISO8601_DATETIME_SCHEMA
which matches regex '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z'. This can be
parsed with just a datetime.strptime(): iso8601 module is not needed.

* Add formats.expiry_string_to_datetime() helper function
* Modify the 3 locations that used iso8601 and the api/metadata.py usage
  of datetime.strptime()
* Remove related unnecessary logger setup
* Add the missing exception documentation to relevant functions (in many
  cases the exception is rather unlikely as the schema has been verified
  many times before this though...)

Fixes theupdateframework#1065

Signed-off-by: Jussi Kukkonen <[email protected]>
@trishankatdatadog
Copy link
Member

I think the paragraph is fairly clear but first sentence could be better: we want to support only this very specific format of the ISO8601 not the whole iso8601 spec.

Great observation. Please send a PR to the spec 🙂

jku pushed a commit to jku/python-tuf that referenced this issue Oct 13, 2020
Our 'expires' strings are constrained by the ISO8601_DATETIME_SCHEMA
which matches regex '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z'. This can be
parsed with just a datetime.strptime(): iso8601 module is not needed.

* Add formats.expiry_string_to_datetime() helper function
* Modify the 3 locations that used iso8601 and the api/metadata.py usage
  of datetime.strptime()
* Remove related unnecessary logger setup
* Add the missing exception documentation to relevant functions (in many
  cases the exception is rather unlikely as the schema has been verified
  many times before this though...)

Fixes theupdateframework#1065

Signed-off-by: Jussi Kukkonen <[email protected]>
jku pushed a commit to jku/python-tuf that referenced this issue Oct 13, 2020
Our 'expires' strings are constrained by the ISO8601_DATETIME_SCHEMA
which matches regex '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z'. This can be
parsed with just a datetime.strptime(): iso8601 module is not needed.

* Add formats.expiry_string_to_datetime() helper function
* Modify the 3 locations that used iso8601 and the api/metadata.py usage
  of datetime.strptime()
* Remove related unnecessary logger setup
* Add the missing exception documentation to relevant functions (in many
  cases the exception is rather unlikely as the schema has been verified
  many times before this though...)

Fixes theupdateframework#1065

Signed-off-by: Jussi Kukkonen <[email protected]>
@jku jku mentioned this issue Oct 13, 2020
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Related to the client (updater) implementation enhancement repository Related to the repository implementation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants