-
Notifications
You must be signed in to change notification settings - Fork 4
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
Replace snap channel config for hard coded revision config #79
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
license-eye has checked 91 files.
Valid | Invalid | Ignored | Fixed |
---|---|---|---|
31 | 1 | 59 | 0 |
Click to see the invalid file list
- charms/worker/k8s/templates/snap_installation.yaml
Use this command to fix any missing license headers
```bash
docker run -it --rm -v $(pwd):/github/workspace apache/skywalking-eyes header fix
</details>
channel: | ||
default: edge | ||
type: string | ||
description: Snap channel of the k8s snap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, not going to use snap channels anymore
rm -rf $CRAFT_PRIME/lib | ||
rm -rf $CRAFT_PRIME/lib $CRAFT_PRIME/templates | ||
mv $CRAFT_PRIME/k8s/lib $CRAFT_PRIME/lib | ||
mv $CRAFT_PRIME/k8s/templates $CRAFT_PRIME/templates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This moves the templates folder from ./k8s/templates
to ./templates
once installed on the k8s-worker
units, along with the ./lib
folder
channel: | ||
default: edge | ||
type: string | ||
description: Snap channel of the k8s snap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, revisions are hard coded
@on_error(ops.BlockedStatus("Failed to install k8s snap."), SnapError) | ||
def _install_k8s_snap(self): | ||
"""Install the k8s snap package.""" | ||
@on_error(ops.BlockedStatus("Failed to install snaps."), snap_lib.SnapError) | ||
def _install_snaps(self): | ||
"""Install snap packages.""" | ||
status.add(ops.MaintenanceStatus("Ensuring snap installation")) | ||
log.info("Ensuring k8s snap version") | ||
snap_ensure("k8s", SnapState.Latest.value, self.config["channel"]) | ||
snap_management() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's move all the logic about managing the snap revision to another module
class SnapFileArgument(BaseModel): | ||
"""Structure to install a snap by file. | ||
|
||
Attributes: | ||
install_type (str): literal string defining this type | ||
name (str): The name of the snap after installed | ||
filename (Path): Path to the snap to locally install | ||
classic (bool): If it should be installed as a classic snap | ||
dangerous (bool): If it should be installed as a dangerouse snap | ||
devmode (bool): If it should be installed as with dev mode enabled | ||
""" | ||
|
||
install_type: Literal["file"] = Field("file", alias="install-type", exclude=True) | ||
name: str = Field(exclude=True) | ||
filename: Optional[Path] = None | ||
classic: Optional[bool] = None | ||
devmode: Optional[bool] = None | ||
dangerous: Optional[bool] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like all the arguments in snap_lib.install_local(...)
All the names and type mimic those arguments intentionally.
two arguments name
and install_type
do not exist as arguments to that method -- so they are marked with exclude=True
. When this.dict(...)
is called -- those arguments won't show up in the call
class SnapStoreArgument(BaseModel): | ||
"""Structure to install a snap by snapstore. | ||
|
||
Attributes: | ||
install_type (str): literal string defining this type | ||
name (str): The type of the request. | ||
state (SnapState): a `SnapState` to reconcile to. | ||
classic (bool): If it should be installed as a classic snap | ||
devmode (bool): If it should be installed as with dev mode enabled | ||
channel (bool): the channel to install from | ||
cohort (str): the key of a cohort that this snap belongs to | ||
revision (int): the revision of the snap to install | ||
""" | ||
|
||
install_type: Literal["store"] = Field("store", alias="install-type", exclude=True) | ||
name: str = Field(exclude=True) | ||
classic: Optional[bool] = None | ||
devmode: Optional[bool] = None | ||
state: Optional[snap_lib.SnapState] = Field(snap_lib.SnapState.Present) | ||
channel: Optional[str] = None | ||
cohort: Optional[str] = None | ||
revision: Optional[int] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like all the arguments in snap_lib.Snap.ensure(...)
All the names and types mimic those arguments intentionally.
Again, the two arguments name
and install_type
do not exist as arguments to that method -- so they are marked with exclude=True
. When this.dict(...)
is called -- those arguments won't show up in the call
SnapArgument = Annotated[ | ||
Union[SnapFileArgument, SnapStoreArgument], Field(discriminator="install_type") | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pydantic's fancy way of creating a type which can parse data as either SnapFileArgument
or SnapStoreArgument
the Field(discriminator="install_type")
indicates to the parser how to determine which type of object it should be parsed as.
dpkg_arch = ["dpkg", "--print-architecture"] | ||
arch = subprocess.check_output(dpkg_arch).decode("UTF-8").strip() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the snap_installation.yaml
will have sections based on architecture
. each architecture can specify a list of snaps to install (not just one). This lets us have the flexibility to add multiple snaps in the future if necessary. Maybe we want to add kubelet
with its own set of args. Or maybe by default install k9s
or ponysay
.
raise snap_lib.SnapError(f"Failed to find revision for arch={arch}") | ||
|
||
try: | ||
args: List[SnapArgument] = [parse_obj_as(SnapArgument, arg) for arg in arch_spec] # type: ignore[arg-type] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, for every arg
in the architecture specification (arch_spec
) we want to parse the dict object into either a SnapFileArgument
or a SnapStoreArgument
.
Since we made that nice annotated type we can use parse_obj_as(...)
asking pydantic to figure out which object type it is.
for args in _parse_management_arguments(): | ||
which = cache[args.name] | ||
if isinstance(args, SnapFileArgument) and which.revision != "x1": | ||
snap_lib.install_local(**args.dict(exclude_none=True)) | ||
elif isinstance(args, SnapStoreArgument): | ||
log.info("Ensuring %s snap version", args.name) | ||
which.ensure(**args.dict(exclude_none=True)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This management()
will get called on every reconcile hook. We need to parse the snap_installation.yaml
, determine the args, and either locally install or run ensure
with the given arguments. Pydantic has validated the arguments match what's in snap_lib
, so we can just turn the args back into a dict, and call the methods with keyword arguments.
We don't want to keep installing the local file over and over and over, -- so this checks for the revision x1
but that might not be the cleanest way. This would limit the number of times you can do a install_local
to just one revision. Maybe there's a TODO
needed here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely, let's add a TODO here
- name: k8s | ||
install-type: store | ||
channel: edge | ||
arm64: | ||
- name: k8s | ||
install-type: store | ||
channel: edge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by default, the main
branch will continue installing from the edge channel. Other branches will use specific snap revisions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing work! LGTM
SnapArgument = Annotated[ | ||
Union[SnapFileArgument, SnapStoreArgument], Field(discriminator="install_type") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! I love the discriminator logic here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is what we have to work with in pydantic 1.x. This has been changed in pydantic 2.x a bit
https://docs.pydantic.dev/latest/migration/#introduction-of-typeadapter
Applicable spec:
Overview
Rather than encode the snap revisions into the charm code directly, include a file installed by the charm to specify the revision or channel of the snap. Each charm branch may adjust this file's contents in order to either install by channel or by revision.
Rationale
k8s
andk8s-worker
charm.Checklist
src-docs
urgent
,trivial
,complex
)