File: gcssource.py

package info (click to toggle)
nsscache 0.49-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 19,664 kB
  • sloc: python: 8,661; xml: 584; sh: 304; makefile: 19
file content (195 lines) | stat: -rw-r--r-- 5,916 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
"""An implementation of a GCS data source for nsscache."""

import logging
import warnings

from google.cloud import storage

from nss_cache import error
from nss_cache.maps import group
from nss_cache.maps import passwd
from nss_cache.maps import shadow
from nss_cache.sources import source
from nss_cache.util import file_formats
from nss_cache.util import timestamps

warnings.filterwarnings(
    "ignore", "Your application has authenticated using end user credentials"
)


def RegisterImplementation(registration_callback):
    registration_callback(GcsFilesSource)


class GcsFilesSource(source.Source):
    """Source for data fetched from GCS."""

    # GCS Defaults
    BUCKET = ""
    PASSWD_OBJECT = ""
    GROUP_OBJECT = ""
    SHADOW_OBJECT = ""

    # for registration
    name = "gcs"

    def __init__(self, conf):
        """Initialize the GcsFilesSource object.

        Args:
          conf: A dictionary of key/value pairs.

        Raises:
          RuntimeError: object wasn't initialized with a dict.
        """
        super(GcsFilesSource, self).__init__(conf)
        self._SetDefaults(conf)
        self._gcs_client = None

    def _GetClient(self):
        if self._gcs_client is None:
            self._gcs_client = storage.Client()
        return self._gcs_client

    def _SetDefaults(self, configuration):
        """Set defaults if necessary."""

        if "bucket" not in configuration:
            configuration["bucket"] = self.BUCKET
        if "passwd_object" not in configuration:
            configuration["passwd_object"] = self.PASSWD_OBJECT
        if "group_object" not in configuration:
            configuration["group_object"] = self.GROUP_OBJECT
        if "shadow_object" not in configuration:
            configuration["shadow_object"] = self.SHADOW_OBJECT

    def GetPasswdMap(self, since=None):
        """Return the passwd map from this source.

        Args:
          since: Get data only changed since this timestamp (inclusive) or None
          for all data.

        Returns:
          instance of passwd.PasswdMap
        """
        return PasswdUpdateGetter().GetUpdates(
            self._GetClient(), self.conf["bucket"], self.conf["passwd_object"], since
        )

    def GetGroupMap(self, since=None):
        """Return the group map from this source.

        Args:
          since: Get data only changed since this timestamp (inclusive) or None
          for all data.

        Returns:
          instance of group.GroupMap
        """
        return GroupUpdateGetter().GetUpdates(
            self._GetClient(), self.conf["bucket"], self.conf["group_object"], since
        )

    def GetShadowMap(self, since=None):
        """Return the shadow map from this source.

        Args:
          since: Get data only changed since this timestamp (inclusive) or None
          for all data.

        Returns:
          instance of shadow.ShadowMap
        """
        return ShadowUpdateGetter().GetUpdates(
            self._GetClient(), self.conf["bucket"], self.conf["shadow_object"], since
        )


class GcsUpdateGetter(object):
    """Base class that gets updates from GCS."""

    def __init__(self):
        self.log = logging.getLogger(__name__)

    def GetUpdates(self, gcs_client, bucket_name, obj, since):
        """Gets updates from a source.

        Args:
          gcs_client: initialized gcs client
          bucket_name: gcs bucket name
          obj: object with the data
          since: a timestamp representing the last change (None to force-get)

        Returns:
            A tuple containing the map of updates and a maximum timestamp
        """
        bucket = gcs_client.bucket(bucket_name)
        blob = bucket.get_blob(obj)
        # get_blob captures NotFound error and returns None:
        if blob is None:
            self.log.error("GCS object gs://%s/%s not found", bucket_name, obj)
            raise error.SourceUnavailable("unable to download object from GCS.")
        # GCS doesn't return HTTP 304 like HTTP or S3 sources,
        # so return if updated timestamp is before 'since':
        if since and timestamps.FromDateTimeToTimestamp(blob.updated) < since:
            return []

        data_map = self.GetMap(cache_info=blob.open())
        data_map.SetModifyTimestamp(timestamps.FromDateTimeToTimestamp(blob.updated))
        return data_map

    def GetParser(self):
        """Return the approriate parser.

        Must be implemented by child class.
        """
        raise NotImplementedError

    def GetMap(self, cache_info):
        """Creates a Map from the cache_info data.

        Args:
          cache_info: file-like object containing the data to parse

        Returns:
          A child of Map containing the cache data.
        """
        return self.GetParser().GetMap(cache_info, self.CreateMap())


class PasswdUpdateGetter(GcsUpdateGetter):
    """Get passwd updates."""

    def GetParser(self):
        """Returns a MapParser to parse FilesPasswd cache."""
        return file_formats.FilesPasswdMapParser()

    def CreateMap(self):
        """Returns a new PasswdMap instance to have PasswdMapEntries added to it."""
        return passwd.PasswdMap()


class GroupUpdateGetter(GcsUpdateGetter):
    """Get group updates."""

    def GetParser(self):
        """Returns a MapParser to parse FilesGroup cache."""
        return file_formats.FilesGroupMapParser()

    def CreateMap(self):
        """Returns a new GroupMap instance to have GroupMapEntries added to it."""
        return group.GroupMap()


class ShadowUpdateGetter(GcsUpdateGetter):
    """Get shadow updates."""

    def GetParser(self):
        """Returns a MapParser to parse FilesShadow cache."""
        return file_formats.FilesShadowMapParser()

    def CreateMap(self):
        """Returns a new ShadowMap instance to have ShadowMapEntries added to it."""
        return shadow.ShadowMap()