Skip to content

Commit

Permalink
[WIP] Add support to cloud_init to add win_init.
Browse files Browse the repository at this point in the history
win_init will add the ability to do some initialization to windows based AMIs.
Add support to Change the Administrator password - todo: still need to encrypt this.
Add support to enable WinRM - todo: need to update this to enable winrm over https instead (or as well).
  • Loading branch information
Nick Lang committed Feb 20, 2024
1 parent 153bb29 commit c1dd486
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 18 deletions.
1 change: 1 addition & 0 deletions carthage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
base_injector.add_provider(InjectionKey(carthage.ssh.SshAgent), carthage.ssh.ssh_agent)
base_injector.add_provider(carthage.pki.PkiManager)
base_injector(carthage.cloud_init.enable_cloud_init_plugins)
base_injector(carthage.cloud_init.enable_win_init_plugins)

__all__ += ['base_injector']

Expand Down
71 changes: 53 additions & 18 deletions carthage/cloud_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

__all__ = []


@dataclasses.dataclass
class CloudInitConfig:

Expand All @@ -30,7 +29,10 @@ class CloudInitConfig:

network_configuration: dict = dataclasses.field(default_factory=lambda: {})

__all__ += ['CloudInitConfig']
class WinInitConfig(CloudInitConfig):
pass

__all__ += ['CloudInitConfig', 'WinInitConfig']


@inject_autokwargs(model=AbstractMachineModel)
Expand All @@ -48,43 +50,61 @@ def default_class_injection_key(cls):
def name(self):
raise NotImplementedError


__all__ += ['CloudInitPlugin']

@inject_autokwargs(model=AbstractMachineModel)
class WinInitPlugin(CloudInitPlugin):

@classmethod
def default_class_injection_key(cls):
return InjectionKey(WinInitPlugin, name=cls.name)


__all__ += ['WinInitPlugin']


@inject(model=AbstractMachineModel,
ainjector=AsyncInjector)
async def generate_cloud_init_cloud_config(*, ainjector, model):
'''
Generate a :class:`CloudInitConfig` from the model. The operation is based on the *cloud_init* property in the model:
Generate a :class:`CloudInitConfig` from the model.
The operation is based on the *cloud_init* or the *win_init* property in the model:
* True: Generate cidata based on calling all the :class:`CloudInitPlugins <CloudInitPlugin>` registered with the model's injector.
* True: Generate cidata based on calling all the :class:`CloudInitPlugins <CloudInitPlugin>` registered with the model's injector.
* False: return None
* False: return None
* A :class:`CloudInitConfig` object: use that object without calling plugins.
* A :class:`CloudInitConfig` object: use that object without calling plugins.
'''

cloud_init = getattr(model, 'cloud_init', False)
if not cloud_init:
win_init = getattr(model, 'win_init', False)
if not cloud_init and not win_init:
return

if cloud_init and win_init:
raise ValueError("Can not do both cloud_init and win_init at the same time.")

if cloud_init is True:
config = CloudInitConfig()
plugin_data = await ainjector.filter_instantiate_async(CloudInitPlugin, ['name'], ready=True)
for plugin_key, plugin in plugin_data:
# we apply in order so that plugins can look at previous
# results. We sacrifice parallelism to get this.
await plugin.apply(config)
if not plugin_data:
return
return config
elif win_init is True:
config = CloudInitConfig()
plugin_data = await ainjector.filter_instantiate_async(WinInitPlugin, ['name'], ready=True)
else:
return model.cloud_init
return False

for _, plugin in plugin_data:
# we apply in order so that plugins can look at previous
# results. We sacrifice parallelism to get this.
await plugin.apply(config)
if not plugin_data:
return
return config

__all__ += ['generate_cloud_init_cloud_config']


@inject(model=AbstractMachineModel, ainjector=AsyncInjector,
config_layout=ConfigLayout)
async def generate_cloud_init_cidata(
Expand Down Expand Up @@ -210,5 +230,20 @@ def enable_cloud_init_plugins(injector):
injector.add_provider(NetworkPlugin, allow_multiple=True)
injector.add_provider(HostnamePlugin, allow_multiple=True)

class AdminPasswordPlugin(WinInitPlugin):
name = "admin_password"
async def apply(self, config: CloudInitConfig):
config.user_data['password'] = self.model.admin_password


class WinRmPlugin(WinInitPlugin):
name = "enable_winrm"
async def apply(self, config: CloudInitConfig):
config.user_data['winrm'] = self.model.enable_winrm

@inject(injector=Injector)
def enable_win_init_plugins(injector):
injector.add_provider(AdminPasswordPlugin, allow_multiple=True)
injector.add_provider(WinRmPlugin, allow_multiple=True)

__all__ += ['enable_cloud_init_plugins']
__all__ += ['enable_cloud_init_plugins', 'enable_win_init_plugins']

0 comments on commit c1dd486

Please sign in to comment.