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

API/ABI: restrict the symbol export to a determined public API #227

Merged
merged 5 commits into from
Sep 30, 2021

Conversation

schopin-pro
Copy link
Contributor

@schopin-pro schopin-pro commented Sep 15, 2021

Description

The goal of this PR is to properly determine what the public interface of libnetplan is. There is potentially a bit of ABI breakage, as symbols now default to hidden status unless explicitly marked otherwise, and said marking was done manually by looking at the current consumers I had on my system and trying to fill in the blanks.

The API, on the other hand, has been conscientiously broken, as most of what was exposed in the headers are now safely hidden away in our internal headers. This is by design, and AFAICT should not break existing code.

This PR depends on #230, as denoted by the merge commit in there. It should make independent review a bit easier :).

Given the fact that the main purpose of this PR is to limit the amount of symbols we export, we can safely assume that strictly speaking, there is ABI breakage. However, the whole point was to only expose the symbols that are in actual use out there in the real world.

As usual, the patchset has been edited expecting a review commit by commit, as the whole diff can be a bit daunting.

Checklist

  • Runs make check successfully.
  • Retains 100% code coverage (make check-coverage).
  • New/changed keys in YAML format are documented.

@schopin-pro
Copy link
Contributor Author

The tests don't pass yet, there are a couple symbols missing.

@codecov-commenter
Copy link

codecov-commenter commented Sep 16, 2021

Codecov Report

Merging #227 (fd5c988) into main (76ae706) will not change coverage.
The diff coverage is n/a.

❗ Current head fd5c988 differs from pull request most recent head e0b34cc. Consider uploading reports for the commit e0b34cc to get more accurate results
Impacted file tree graph

@@           Coverage Diff           @@
##             main     #227   +/-   ##
=======================================
  Coverage   99.05%   99.05%           
=======================================
  Files          57       57           
  Lines        9633     9633           
=======================================
  Hits         9542     9542           
  Misses         91       91           
Impacted Files Coverage Δ
src/dbus.c 100.00% <ø> (ø)
src/error.c 100.00% <ø> (ø)
src/generate.c 100.00% <ø> (ø)
src/netplan.c 100.00% <ø> (ø)
src/networkd.c 100.00% <ø> (ø)
src/nm.c 99.46% <ø> (ø)
src/openvswitch.c 100.00% <ø> (ø)
src/parse-nm.c 100.00% <ø> (ø)
src/parse.c 100.00% <ø> (ø)
src/sriov.c 100.00% <ø> (ø)
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 76ae706...e0b34cc. Read the comment docs.

@schopin-pro schopin-pro marked this pull request as ready for review September 24, 2021 16:43
@schopin-pro schopin-pro changed the title API/ABI cleanup API/ABI: restrict the symbol export to a determined public API Sep 24, 2021
@schopin-pro schopin-pro force-pushed the api-abi-cleanup branch 3 times, most recently from ed2da6a to 39fb87d Compare September 28, 2021 06:46
@slyon
Copy link
Collaborator

slyon commented Sep 28, 2021

Pushed a rebase on top of the latest origin/main

@schopin-pro schopin-pro force-pushed the api-abi-cleanup branch 2 times, most recently from 8c65ea7 to fd5c988 Compare September 29, 2021 09:19
Copy link
Collaborator

@slyon slyon left a comment

Choose a reason for hiding this comment

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

Alright, that took longer than expected. I really needed to wrap my head around this new structure of the headers, but I really like it and think it does not break existing API consumers.

We should reconsider the parse-helpers.h filename though. Maybe it can be merged with util-interna.h? See inline comment.

Other than that it's mostly formatting nitpicks (sorry for that!)

Next, I want to run the integration tests suite, using new libnetplan (incl. these commits) with old binaries, to confirm that the ABI is still stable.

Makefile Outdated Show resolved Hide resolved
include/parse.h Outdated Show resolved Hide resolved
src/parse-helpers.h Outdated Show resolved Hide resolved
src/openvswitch.h Outdated Show resolved Hide resolved
src/types.h Show resolved Hide resolved
src/networkd.h Outdated Show resolved Hide resolved
src/nm.h Outdated Show resolved Hide resolved
src/openvswitch.h Outdated Show resolved Hide resolved
src/sriov.h Outdated Show resolved Hide resolved
src/validation.h Outdated Show resolved Hide resolved
This work involves splitting out some things from those headers into new
internal headers, and moving definitions around so that the public
headers are as self-contained as possible.

For the new internal headers, I decided to have a big "types.h" header
containing all the types used in a network definition. In itself, these
types aren't related to parsing except that the parser module is the
only producer, so I decided they could live on their own. This is also
the place where type-specific helpers can be found, such as
reset_netdef().

The various macros used to generate YAML were gathered up from both
source headers into a new yaml-helpers.h header, whereas the various
global state definitions were split off into their own headers, making
it easier to spot which areas of the code still rely on global state.
The remainder functions were moved into util-internal.h

This allows us to minimize the API exposed to the outside world.

V2:
  * Remove the extraneous struct net_definition line, as it is redundant
    with the typedef
  * Normalize all the symbol declarations to have the symbol name at the
    beginning of their own line, at least in the headers we touched.

V3:
  * Remove extraneous stdbool.h header
  * Split parse-helpers.h into yaml-helpers.h and util-internal.h
  * Add uuid as a dependency of the dbus generator to make it compile
    with the new lay of the land.
  * Add some missing copyright headers
This patch cleanly marks which functions/objects are part of the public
library API, and which are meant to be consumed by our own binaries.

In order to reduce as much as possible the ABI dependencies between our
binaries and the library, I move the various generator modules into the
library as it make sense for them to access directly the objects. On the
other hand, both generate.c and dbus.c should be relatively trivial to
change to use getters and setters instead of direct structure access.

Those changes will be the object of later patches.

V3:
  * Makefile: consolidate the pkg-config calls and add back the LDFLAGS
    variable
  * Normalize the touched function declarations to always have the
    symbol name at the start of the line.
Re-export (and recreate) various symbols that are needed by the
`generate` binary as shipped in the Ubuntu package 0.103-0ubuntu5

I have chosen a separate marker for those, in order to distinguish what
was exposed deliberately and by accident. That way, if we need to do a
SONAME bump, we can clean up the symbols marked with NETPLAN_ABI.

V3:
  * Normalize the declarations that are touched to always have the
    symbol name at the start of the line
Only those marked by NETPLAN_{PUBLIC,INTERNAL,ABI} will be available to
the loader.
@schopin-pro
Copy link
Contributor Author

The V3 contains mostly cosmetic fixes, addressing @slyon's concerns.

@slyon
Copy link
Collaborator

slyon commented Sep 30, 2021

Tests are looking very good and ABI seems to be stable.
I've built a custom netplan package (103-0ubuntu7), based upon 0.103-0ubuntu6, that contains the changes from #228 #229 #230 and #227 and used the libnetplan0 and libnetplan-dev binary packages of that newer version to run the intgration-/autopkgtests of 0.103-0ubuntu6 against.

user@machine:packaging-netplan(ubuntu/master)$ time autopkgtest ../libnetplan0_0.103-0ubuntu7_amd64.deb ../libnetplan-dev_0.103-0ubuntu7_amd64.deb netplan.io -U -- qemu /data/autopkgtest-impish-amd64.img 
autopkgtest [12:23:52]: starting date: 2021-09-30
autopkgtest [12:23:52]: version 5.16ubuntu1
[...]
autopkgtest [13:18:52]: @@@@@@@@@@@@@@@@@@@@ summary
ovs                  PASS
ethernets            PASS
bridges              PASS
bonds                PASS
routing              PASS
vlans                PASS
wifi                 FLAKY non-zero exit status 1
tunnels              PASS
scenarios            PASS
regressions          PASS
autostart            PASS
cloud-init           PASS
qemu-system-x86_64: terminating on signal 15 from pid 59394 (/usr/bin/python3)

real	55m0,015s
user	30m7,137s
sys	2m2,651s

Full logs: https://paste.ubuntu.com/p/VFVyYT33Gn/

Copy link
Collaborator

@slyon slyon left a comment

Choose a reason for hiding this comment

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

Thank a lot for your comments and the cleanups!

This is a big refactoring and I'm very happy that you have been able to implement it without breaking the existing ABI and API (at least those symbols that are actually being used by external consumers). Kudos!

We can have a properly defined public API now, hiding all the non-relevant symbols.

@slyon slyon merged commit 467e88a into canonical:main Sep 30, 2021
slyon pushed a commit that referenced this pull request Nov 24, 2021
Instead of directly writing into the final network state, use a separate
global parser object to store any intermediate data needed during
parsing. This allows us to ensure that the client can only see valid
configurations, as the parser is opaque to the client, who then must
call our functions to import and validate the data into the final
network state.

Since the public API doesn't allow us to pass around some context, we
still store a static object to use in those APIs, but all the internal
functions shouldn't touch any external data.

This PR depends on #229 and #227 and is only a prequel to the proper YAML
parser unification work, which will start with the removal of the current parsing
API, replaced by one based on explicitly allocated states, in conjunction with
the #232 work. To make things easier, the latter PR has been added as a dependency
for this one.

It also picked up a dependency on #234 along the way :)

Only the commits after the merge commit are part of the PR proper, the rest are
from the dependencies.

COMMITS:
* lib: new functions *_clear to safely clear heap-allocated complex structs

We already have the functions to actually free the objects, these
helpers just wrap those to also clear up the *pointer* and nullifying
it.

These functions are going to be used in the parser code to clear
intermediate state, notably on the error paths.

Function names, signature and implementations are heavily inspired by
g_datalist_clear().

V2:
rename the functions to match the new scheme [prefix_]type_action

* parse: clear the cur_addr_options pointer once the parsing is done

Keeping a pointer around after we've transferred ownership of the data
to a netdef is a recipy for disaster, as we have no way of knowing if
we're still responsible for cleaning up the NetplanAddressOptions
object.

* lib: yaml: change the error goto label to err_path

`error` is usually a GError** argument, and the new name is just as
legible.

* lib: write_netplan_conf_full: use an early-return

This allows us to shift the code left, make it easier to read.

* lib: util: netplan_delete_connection: clear the state at the beginning

The function is supposed to work on an already empty state, as it parses
a whole tree and validates it afterwards. We thus clear it explicitly.

This is a latent bug that might become apparent should the validation be
a little more stringent, such as redefinition of already existing
netdefs.

* tests: clear the global state in teardowns to expose inter-test order dependency

Some tests were relying on the state having been cleared in a previous
state, which isn't necessarily the case.

* cli/utils: force a full parse cycle before extracting ids by devtype

While the current approach works for now, it'll break when we separate
parser state and global state, until we do the actual validation.

* lib: use an explicit parser context

Instead of directly writing into the final network state, use a separate
global parser object to store any intermediate data needed during
parsing. This allows us to ensure that the client can only see valid
configurations, as the parser is opaque to the client, who then must
call our functions to import *and validate* the data into the final
network state.

Since the public API doesn't allow us to pass around some context, we
still store a static object to use in those APIs, but all the internal
functions shouldn't touch any external data.

V2:
* Remove stray cur_filename global variable
* Fix some formatting
* Rename 'done' field to 'parsed_defs'
* Reduced the nocov zone

* libnetplan: rewrite netplan_finish_parse using explicit states

The netplan_finish_parse implementation is moved to our ABI attic,
abi_compat.c, and is now a simple wrapper around
netplan_state_import_parser_results, which is the new function used to
import into a Netplan state the results of a parsing operations. As
before, the idea is to maintain a constantly valid Netplan state, so
this function does validation before import.

* lib: expose new functions to generate YAML from Netplan state or defs

The old functions have been rewritten as wrappers around the new APIs

V2:
* Add some header comments to the new functions

* parse-nm: new API using the separate parser state

* lib: add accessors to be able to reach filename from netdef ID

* lib: rewrite process_input_file and process_yaml_hierarchy using the new API scheme

There are no process_input_file() replacement as it can very well be
written by the client code. process_yaml_hierarchy() has been replaced
by a function in utils, netplan_parser_load_yaml_hierarchy(), which
leaves the error policy up to the client code rather than calling
exit().

The old versions have been moved to abi_compat.c and reimplemented to
keep their previous semantics.

* lib: move _write_netplan_conf to the abi_compat.c file

This function is only there for legacy reasons, and should be replaced
by a normalized API.

* lib: netplan_get_filename_by_id: move to abi_compat.c

This is an old API. The ABI compat version is entirely reimplemented
using public, up-to-date functions.

* lib: util: reimplement netplan_delete_connection with the new APIs

This new implementation does away with global state, creating its own
local state instead.

V2: fix formatting

* netplan:utils: mark the exception path of the devtype iter as nocover

* netplan/cli/utils: move the iter ABI check in the wrapper class

This makes it possible to use the wrapper class in autonomy, and remove
the implicit dependency between the netplan_get_ids_for_devtype tests
and the _NetdefIdIterator tests. The latter would crash if run before
the former, as the ctypes bindings wouldn't have been initialized.

* lib: networkd: fix a stray call to an old API

THis call meant that the network file was generated using the global
state as reference instead of the local np_state, making things such as
the VLAN feature basically useless.

* generate: migrate to the new libnetplan APIs

This allows us to do proper cleanup on error cases.

Note that since generate was the only consumer of some of the legacy
APIs, those endpoints are not hit by the testsuite anymore and are thus
marked as ignored by the coverage calculations.
@schopin-pro schopin-pro mentioned this pull request Feb 15, 2022
5 tasks
@schopin-pro schopin-pro deleted the api-abi-cleanup branch April 13, 2022 09:04
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