Coverage for r11k/puppetmodule/base.py: 82%

27 statements  

« prev     ^ index     » next       coverage.py v7.2.1, created at 2023-03-13 23:29 +0100

1""" 

2Abstract base class for Puppet module sources. 

3 

4Puppet modules can come from many places. Here we provide a common 

5interface for how the rest of the program should be able to interact 

6with them. A few implementation parts are here, for things that will 

7be common among almost all module providers. 

8""" 

9 

10from typing import ( 

11 Any, 

12 Optional, 

13) 

14from semver import VersionInfo 

15from r11k.puppet import PuppetMetadata 

16import r11k.config 

17 

18 

19class PuppetModule: 

20 r""" 

21 Abstract base class for all puppet modules. 

22 

23 Each implementation *must* implement the following: 

24 - `_fetch_metadata` 

25 - `publish` 

26 - `versions` 

27 

28 ##### Protected fields 

29 ###### `_fetch_metadata` 

30 A method which retrieves the metadata from *somewhere*. The 

31 property `metadata` by default caches this information. 

32 

33 :param name: Name of the module. Further meaning depends on the 

34 implementation. 

35 :param version: Version to use. Valid values depend on the 

36 implementation. 

37 :param config: Runtime configuration which may be used by 

38 implementations to aid them in retrieving a module. 

39 """ 

40 

41 # pragma: no cover 

42 def __init__(self, 

43 name: str, 

44 *, 

45 version: Optional[str] = None, 

46 config: r11k.config.Config = r11k.config.config): 

47 self.name = name 

48 self._og_name = name 

49 self.version: Optional[str] = version 

50 self._metadata: Optional[PuppetMetadata] = None 

51 self.config = config 

52 self.explicit: bool = False 

53 

54 @property 

55 def metadata(self) -> PuppetMetadata: 

56 """ 

57 Property containing the modules metadata. 

58 

59 The metadata almost always needs to be fetched from an 

60 external source. Each implementation must implement 

61 `_fetch_metadata` which solves this, while this property 

62 ensures that `_fetch_metadata` is only called once. 

63 """ 

64 if not self._metadata: 

65 self._metadata = self._fetch_metadata() 

66 return self._metadata 

67 

68 def _fetch_metadata(self) -> PuppetMetadata: # pragma: no cover 

69 """Fetch metadata for a given module.""" 

70 raise NotImplementedError() 

71 

72 def publish(self, path: str) -> None: # pragma: no cover 

73 """ 

74 Publish this module to the given path. 

75 

76 [Parameters] 

77 path - absolute path to modules own directory, for example: 

78 /etc/puppetlabs/code/environments/{env_name}/modules/{module_name} 

79 

80 :raises: `NotImplementedError` 

81 """ 

82 raise NotImplementedError() 

83 

84 def versions(self) -> list[VersionInfo]: # pragma: no cover 

85 """ 

86 List available versions of this module. 

87 

88 :raises: `NotImplementedError` 

89 """ 

90 raise NotImplementedError() 

91 

92 def latest(self) -> VersionInfo: 

93 """Return the latest available version.""" 

94 return max(self.versions()) 

95 

96 @property 

97 def module_path(self) -> str: 

98 """Where module is located in file system.""" 

99 parts = self.name.split('-', 1) 

100 if len(parts) == 1: 

101 return parts[0] 

102 else: 

103 return parts[1] 

104 

105 def serialize(self) -> dict[str, Any]: 

106 """ 

107 Serialize back into form from puppetfile.yaml. 

108 

109 Includes name, and version if set. 

110 """ 

111 return { 

112 'name': self.name, 

113 **({'version': self.version} 

114 if self.version else {}) 

115 } 

116 

117 def __repr__(self) -> str: # pragma: no cover 

118 return f"PuppetModule('{self.name}', '{self.version}')"