Skip to content

Commit 71f8b85

Browse files
authored
Allow to use EPOCH timestamp in the image annotation (#665)
For the images that were build without commit time or hash data we allow to annotate them for the Commit Time exporter to work properly. This change allows to annotate with EPOCH time format. Signed-off-by: Michal Pryc <mpryc@redhat.com> Signed-off-by: Michal Pryc <mpryc@redhat.com>
1 parent 37cc199 commit 71f8b85

4 files changed

Lines changed: 77 additions & 6 deletions

File tree

docs/Configuration.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ Example image metadata with `io.openshift.build.commit.date` Docker Labels and `
477477
```
478478
IMAGE_SHA=588fb67a63ccbadf245b6d30747c404d809a851551b67c615a18217bf443a78e
479479

480-
$ oc describe "sha256:${IMAGE_SHA}"
480+
$ oc describe image "sha256:${IMAGE_SHA}"
481481

482482
Docker Image: image-registry.openshift-image-registry.svc:5000/mongo-persistent/todolist-mongo-go@sha256:588fb67a63ccbadf245b6d30747c404d809a851551b67c615a18217bf443a78e
483483
Name: sha256:588fb67a63ccbadf245b6d30747c404d809a851551b67c615a18217bf443a78e
@@ -522,6 +522,21 @@ $ oc annotate image "sha256:${IMAGE_SHA}" --overwrite \
522522
> image.image.openshift.io/sha256:588fb67a63ccbadf245b6d30747c404d809a851551b67c615a18217bf443a78e annotated
523523
```
524524
525+
The Image may be also annotated with 10 digit EPOCH timestamp. Allowed format is one, where milliseconds are ignored:
526+
527+
```
528+
$ EPOCH_TIMESTAMP=`git log -1 --format=%ct`
529+
$ echo ${EPOCH_TIMESTAMP}
530+
1663770655
531+
532+
$ NAME=my-application
533+
$ IMAGE_SHA=588fb67a63ccbadf245b6d30747c404d809a851551b67c615a18217bf443a78e
534+
$ oc label image "sha256:${IMAGE_SHA}" "app.kubernetes.io/name=${NAME}"
535+
536+
$ oc annotate image "sha256:${IMAGE_SHA}" --overwrite \
537+
io.openshift.build.commit.date="${EPOCH_TIMESTAMP}"
538+
```
539+
525540
### Configuring JIRA workflow(s)
526541
527542
#### Default JIRA workflow

exporters/committime/collector_image.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import pelorus
2222
from committime import CommitMetric
23-
from pelorus.timeutil import parse_guessing_timezone_DYNAMIC
23+
from pelorus.timeutil import parse_guessing_timezone_DYNAMIC, to_epoch_from_string
2424
from pelorus.utils import collect_bad_attribute_path_error, get_env_var, get_nested
2525

2626
from .collector_base import COMMIT_DATE_ANNOTATION_ENV, AbstractCommitCollector
@@ -108,7 +108,10 @@ def commit_metric_from_image(self, app: str, image, errors: list) -> CommitMetri
108108
metric = self._set_commit_time_from_annotations(metric, errors)
109109

110110
if not metric.commit_hash:
111-
# We ignore all the errors by passing "None" as a string, because commit hash isn't required.
111+
# We ignore all the errors by passing [], because commit hash isn't required.
112+
metric = self._set_commit_hash_from_annotations(metric, [])
113+
if not metric.commit_hash:
114+
# We ensure None is passed as string
112115
metric.commit_hash = "None"
113116
metric = self._set_commit_timestamp(metric, errors)
114117

@@ -121,9 +124,15 @@ def _set_commit_timestamp(self, metric: CommitMetric, errors) -> CommitMetric:
121124
# Only convert when commit_time is in metric, previously should be
122125
# found from the Label with fallback to annotation
123126
if metric.commit_time:
124-
metric.commit_timestamp = parse_guessing_timezone_DYNAMIC(
125-
metric.commit_time, format=self._timedate_format
126-
).timestamp()
127+
try:
128+
metric.commit_timestamp = to_epoch_from_string(
129+
metric.commit_time
130+
).timestamp()
131+
except (ValueError, AttributeError):
132+
# Do nothing here as we tried with EPOCH timestamp
133+
metric.commit_timestamp = parse_guessing_timezone_DYNAMIC(
134+
metric.commit_time, format=self._timedate_format
135+
).timestamp()
127136
return metric
128137

129138
def get_commit_time(self, metric) -> Optional[CommitMetric]:

exporters/pelorus/timeutil.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,24 @@ def parse_guessing_timezone_DYNAMIC(timestring: str, format: str) -> datetime:
6262
return parsed.replace(tzinfo=timezone.utc)
6363

6464

65+
def to_epoch_from_string(timestring: str) -> datetime:
66+
"""
67+
It's really EPOCH to EPOCH conversion with datetime return object.
68+
69+
If timestring matches expected EPOCH format a proper datetime object is returned,
70+
otherwise raises ValueError and consumer of this function probably want's to use
71+
other format of timestamp.
72+
"""
73+
epoch_date_time = timestring.split(".")[0]
74+
# Try to convert to an EPOCH, but only if it's 10 digit
75+
if len(epoch_date_time) != 10:
76+
raise ValueError(
77+
f"Tried to get epoch from not allowed string length: {timestring}"
78+
)
79+
else:
80+
return datetime.fromtimestamp(int(epoch_date_time))
81+
82+
6583
def second_precision(dt: datetime) -> datetime:
6684
"""
6785
Change the datetime to have second precision (removing microseconds).

exporters/tests/test_datetime.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from datetime import datetime, timedelta, timezone
22

3+
import pytest
4+
35
import provider_common.github as github
46
from pelorus.timeutil import (
57
parse_assuming_utc,
68
parse_guessing_timezone_DYNAMIC,
79
parse_tz_aware,
10+
to_epoch_from_string,
811
)
912

1013

@@ -102,3 +105,29 @@ def test_github():
102105
actual_unix = github.parse_datetime(TIMESTRING).timestamp()
103106

104107
assert actual_unix == EXPECTED_UNIX
108+
109+
110+
@pytest.mark.parametrize(
111+
"timestamps, expected",
112+
[
113+
("1652305808.000", "1652305808.0"),
114+
("1652305808.122", "1652305808.0"),
115+
("1652305808", "1652305808.0"),
116+
("1652305808.0", "1652305808.0"),
117+
],
118+
)
119+
def test_to_epoch_from_string(timestamps, expected):
120+
epoch_from_str = to_epoch_from_string(timestamps)
121+
assert str(epoch_from_str.timestamp()) == expected
122+
123+
124+
@pytest.mark.xfail(raises=ValueError)
125+
@pytest.mark.parametrize("timestamps", ["1652305803822.0", "1652305", "112322142321"])
126+
def test_to_epoch_from_string_bad_value(timestamps):
127+
to_epoch_from_string(timestamps)
128+
129+
130+
@pytest.mark.xfail(raises=AttributeError)
131+
@pytest.mark.parametrize("timestamps", [1652305808, None])
132+
def test_to_epoch_from_string_bad_arg(timestamps):
133+
to_epoch_from_string(timestamps)

0 commit comments

Comments
 (0)