-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #38 from siemens/develop
Develop
- Loading branch information
Showing
9 changed files
with
575 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Based on https://www.redhat.com/sysadmin/podman-inside-container | ||
ARG FEDORA_TAG | ||
|
||
FROM fedora:${FEDORA_TAG} | ||
RUN dnf -y install \ | ||
procps systemd podman fuse-overlayfs \ | ||
--exclude container-selinux && \ | ||
dnf clean all && \ | ||
rm -rf /var/cache /var/log/dnf* /var/log/yum.* && \ | ||
systemctl enable podman.socket | ||
CMD [ "/usr/sbin/init" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
Package podmannet implements a Gostwire decorator that discovers podman (v4+) | ||
managed networks and then decorates their corresponding Linux-kernel network | ||
interfaces. Supported types of podman networks are “bridge” and “macvlan”. | ||
In case of “bridge” networks this decorator assigns network names as alias names | ||
to the corresponding Linux-kernel bridges and also as a Gostwire-specific label. | ||
For “MACVLAN” networks this decorator assigns the network names as alias names | ||
to the “parent” network interface (or “master” in Linux parlance). | ||
This decorator also copies any network labels it finds into the corresponding | ||
network.Interface instances in a Gostwire discovery information model. | ||
# Note | ||
The Docker-compatible podman API is subtly incompatible: it uses a different | ||
bridge name-allocating method, and it doesn't reveal the bridge and macvlan | ||
master names. | ||
In consequence, we need to resort to a self-rolled minimal HTTP-over-UDS client | ||
that supports a minimal subset of the podman-proprietary libpod API. As of | ||
podman v4 the libpod API endpoint returns network information. As a nice | ||
benefit, the network information endpoint abstracts from the different podmen | ||
networking mechanisms, that is, CNI-based and/or [netavark]-based. | ||
[netavark]: https://github.com/containers/netavark | ||
*/ | ||
package podmannet |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
// (c) Siemens AG 2024 | ||
// | ||
// SPDX-License-Identifier: MIT | ||
|
||
package podmannet | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"net" | ||
"net/http" | ||
"net/url" | ||
"path" | ||
"time" | ||
) | ||
|
||
// UserAgent specifies the HTTP agent string used when talking to podman's | ||
// libpod API. | ||
const UserAgent = "Gostwire (The Sequel)" | ||
|
||
// Client is a minimalist HTTP-over-UDS (unix domain socket) client for | ||
// conversing with podmen libpod API endpoints. | ||
type Client struct { | ||
httpClient *http.Client | ||
endpointURL *url.URL | ||
libpodVersion string // libpod API semver, without "v" prefix. | ||
} | ||
|
||
// newLibpodClient returns a new podman libpod API client. The endpoint must be | ||
// using the "unix" protocol. | ||
// | ||
// Please note that this libpod API client is absolutely minimalist and just | ||
// suffices for querying the podman-managed networks. | ||
func newLibpodClient(endpoint string) (*Client, error) { | ||
epurl, err := url.Parse(endpoint) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid endpoint, reason: %w", err) | ||
} | ||
if epurl.Scheme != "unix" { | ||
return nil, fmt.Errorf("unsupported endpoint protocol '%s'", epurl.Scheme) | ||
} | ||
c := &Client{ | ||
httpClient: &http.Client{ | ||
Transport: &http.Transport{ | ||
DisableCompression: true, | ||
}, | ||
}, | ||
endpointURL: epurl, | ||
} | ||
dialer := &net.Dialer{ | ||
// same as Docker's unix socket default transport configuration, see | ||
// also | ||
// https://github.com/docker/go-connections/blob/fa09c952e3eadbffaf8afc5b8a1667158ba38ace/sockets/sockets.go#L11 | ||
Timeout: 10 * time.Second, | ||
} | ||
c.httpClient.Transport.(*http.Transport).DialContext = func(ctx context.Context, _ string, _ string) (net.Conn, error) { | ||
// we don't want to dial the libpod API endpoint, but instead the engine | ||
// API endpoint as such... | ||
return dialer.DialContext(ctx, epurl.Scheme, epurl.Path) | ||
} | ||
return c, nil | ||
} | ||
|
||
// Close closes idle connections. | ||
func (c *Client) Close() error { | ||
if c.httpClient == nil { | ||
return nil | ||
} | ||
c.httpClient.CloseIdleConnections() | ||
return nil | ||
} | ||
|
||
// apiPath takes a non-versioned libpod API endpoint, such as “/info” and | ||
// “networks/json”; it then returns a versioned libpod path when the libpod | ||
// version is already known, such as “/v1.2.3/libpod/networks/json”. Otherwise | ||
// it returns a “/v0/libpod/...”-based path. In consequence, without the | ||
// libpodVersion set on the Client, only use the “/info” service endpoint, as | ||
// this seems to be version-independent, but still needs any version in its | ||
// endpoint path. | ||
func (c *Client) apiPath(apipath string) string { | ||
if c.libpodVersion == "" { | ||
// use only for initial libpod info (API version) retrieval; please note | ||
// that all libpod API endpoints are versioned, there are not | ||
// un-versioned endpoints like the Docker API does. | ||
return path.Join("/v0/libpod", apipath) | ||
} | ||
return path.Join("/v"+c.libpodVersion+"/libpod", apipath) | ||
} | ||
|
||
// get issues an HTTP GET request for the specified (yet unversioned) API | ||
// endpoint, such as “/networks/json”. It then returns the HTTP response or an | ||
// error. | ||
func (c *Client) get(ctx context.Context, apipath string) (*http.Response, error) { | ||
req, err := http.NewRequestWithContext( | ||
ctx, | ||
http.MethodGet, | ||
"http://localhost"+c.apiPath(apipath), | ||
nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
req.Header.Set("User-Agent", UserAgent) | ||
resp, err := c.httpClient.Do(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { | ||
return nil, fmt.Errorf("podman service returned status code %d", resp.StatusCode) | ||
} | ||
return resp, nil | ||
} | ||
|
||
// ensureReaderClosed helper to drain any service response. | ||
func ensureReaderClosed(resp *http.Response) { | ||
if resp.Body == nil { | ||
return | ||
} | ||
_, _ = io.CopyN(io.Discard, resp.Body, 512) | ||
resp.Body.Close() | ||
} | ||
|
||
// essentialLibpodInformation grabs just the API version information from the | ||
// JSON salad returned by a “/vX/libpod/info” endpoint. | ||
type essentialLibpodInformation struct { | ||
Version struct { | ||
APIVersion string // major.minor.patch, without "v" prefix | ||
} `json:"version"` | ||
} | ||
|
||
// info returns the “essential” libpod information, that is, the libpod API | ||
// version. | ||
func (c *Client) info(ctx context.Context) (essentialLibpodInformation, error) { | ||
resp, err := c.get(ctx, "/info") | ||
var info essentialLibpodInformation | ||
if err != nil { | ||
return info, err | ||
} | ||
err = json.NewDecoder(resp.Body).Decode(&info) | ||
return info, err | ||
} | ||
|
||
// NetworkResource grabs just the few things from a podman network we're | ||
// interested here for the purposes of correctly decorating network interfaces | ||
// with podman network names. We simply ignore all the other JSON salad returned | ||
// from the “/vX/libpod/networks/json” endpoint. | ||
type NetworkResource struct { | ||
Name string `json:"name"` // name of the network | ||
ID string `json:"id"` // unique ID within the particular podman engine instance | ||
Driver string `json:"driver"` // name of the driver; "bridge", "macvlan", "ipvlan" | ||
NetworkInterface string `json:"network_interface"` // name of the associated (master) network interface | ||
Internal bool `json:"internal"` // network is host-internal only, without external connectivity | ||
Labels map[string]string `json:"labels"` | ||
} | ||
|
||
// networkList returns the list of managed podman networks. | ||
func (c *Client) networkList(ctx context.Context) ([]NetworkResource, error) { | ||
var netrscs []NetworkResource | ||
resp, err := c.get(ctx, "/networks/json") | ||
defer ensureReaderClosed(resp) | ||
if err != nil { | ||
return nil, err | ||
} | ||
err = json.NewDecoder(resp.Body).Decode(&netrscs) | ||
return netrscs, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// (c) Siemens AG 2023 | ||
// | ||
// SPDX-License-Identifier: MIT | ||
|
||
package podmannet | ||
|
||
import ( | ||
"testing" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func TestGostwireDecoratorPodmannet(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "ghostwire/decorator/podmannet package") | ||
} |
Oops, something went wrong.