mirror of
https://github.com/helm/helm.git
synced 2026-05-28 04:35:48 -04:00
Modified how created annotation is populated based on package creation time
Signed-off-by: Andrew Block <andy.block@gmail.com>
This commit is contained in:
parent
976ed8c0be
commit
0a69a0dea6
8 changed files with 153 additions and 27 deletions
|
|
@ -29,6 +29,7 @@ import (
|
|||
"helm.sh/helm/v3/internal/tlsutil"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/time/ctime"
|
||||
)
|
||||
|
||||
// OCIPusher is the default OCI backend handler
|
||||
|
|
@ -89,6 +90,9 @@ func (pusher *OCIPusher) push(chartRef, href string) error {
|
|||
path.Join(strings.TrimPrefix(href, fmt.Sprintf("%s://", registry.OCIScheme)), meta.Metadata.Name),
|
||||
meta.Metadata.Version)
|
||||
|
||||
chartCreationTime := ctime.Created(stat)
|
||||
pushOpts = append(pushOpts, registry.PushOptCreationTime(chartCreationTime.Format(time.RFC3339)))
|
||||
|
||||
_, err = client.Push(chartBytes, ref, pushOpts...)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -527,9 +527,9 @@ type (
|
|||
}
|
||||
|
||||
pushOperation struct {
|
||||
provData []byte
|
||||
strictMode bool
|
||||
test bool
|
||||
provData []byte
|
||||
strictMode bool
|
||||
creationTime string
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -583,7 +583,7 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu
|
|||
descriptors = append(descriptors, provDescriptor)
|
||||
}
|
||||
|
||||
ociAnnotations := generateOCIAnnotations(meta, operation.test)
|
||||
ociAnnotations := generateOCIAnnotations(meta, operation.creationTime)
|
||||
|
||||
manifestData, manifest, err := content.GenerateManifest(&configDescriptor, ociAnnotations, descriptors...)
|
||||
if err != nil {
|
||||
|
|
@ -652,10 +652,10 @@ func PushOptStrictMode(strictMode bool) PushOption {
|
|||
}
|
||||
}
|
||||
|
||||
// PushOptTest returns a function that sets whether test setting on push
|
||||
func PushOptTest(test bool) PushOption {
|
||||
// PushOptCreationDate returns a function that sets the creation time
|
||||
func PushOptCreationTime(creationTime string) PushOption {
|
||||
return func(operation *pushOperation) {
|
||||
operation.test = test
|
||||
operation.creationTime = creationTime
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,10 +166,10 @@ func NewRegistryClientWithTLS(out io.Writer, certFile, keyFile, caFile string, i
|
|||
}
|
||||
|
||||
// generateOCIAnnotations will generate OCI annotations to include within the OCI manifest
|
||||
func generateOCIAnnotations(meta *chart.Metadata, test bool) map[string]string {
|
||||
func generateOCIAnnotations(meta *chart.Metadata, creationTime string) map[string]string {
|
||||
|
||||
// Get annotations from Chart attributes
|
||||
ociAnnotations := generateChartOCIAnnotations(meta, test)
|
||||
ociAnnotations := generateChartOCIAnnotations(meta, creationTime)
|
||||
|
||||
// Copy Chart annotations
|
||||
annotations:
|
||||
|
|
@ -190,7 +190,7 @@ annotations:
|
|||
}
|
||||
|
||||
// getChartOCIAnnotations will generate OCI annotations from the provided chart
|
||||
func generateChartOCIAnnotations(meta *chart.Metadata, test bool) map[string]string {
|
||||
func generateChartOCIAnnotations(meta *chart.Metadata, creationTime string) map[string]string {
|
||||
chartOCIAnnotations := map[string]string{}
|
||||
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationDescription, meta.Description)
|
||||
|
|
@ -198,10 +198,12 @@ func generateChartOCIAnnotations(meta *chart.Metadata, test bool) map[string]str
|
|||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationVersion, meta.Version)
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationURL, meta.Home)
|
||||
|
||||
if !test {
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationCreated, helmtime.Now().UTC().Format(time.RFC3339))
|
||||
if len(creationTime) == 0 {
|
||||
creationTime = helmtime.Now().UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationCreated, creationTime)
|
||||
|
||||
if len(meta.Sources) > 0 {
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationSource, meta.Sources[0])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import (
|
|||
|
||||
func TestGenerateOCIChartAnnotations(t *testing.T) {
|
||||
|
||||
nowString := helmtime.Now().Format(time.RFC3339)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
chart *chart.Metadata
|
||||
|
|
@ -43,6 +45,7 @@ func TestGenerateOCIChartAnnotations(t *testing.T) {
|
|||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.created": nowString,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -56,6 +59,7 @@ func TestGenerateOCIChartAnnotations(t *testing.T) {
|
|||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.created": nowString,
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.url": "https://helm.sh",
|
||||
},
|
||||
|
|
@ -76,6 +80,7 @@ func TestGenerateOCIChartAnnotations(t *testing.T) {
|
|||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.created": nowString,
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.url": "https://helm.sh",
|
||||
"org.opencontainers.image.authors": "John Snow",
|
||||
|
|
@ -95,6 +100,7 @@ func TestGenerateOCIChartAnnotations(t *testing.T) {
|
|||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.created": nowString,
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.url": "https://helm.sh",
|
||||
"org.opencontainers.image.authors": "John Snow (john@winterfell.com)",
|
||||
|
|
@ -115,6 +121,7 @@ func TestGenerateOCIChartAnnotations(t *testing.T) {
|
|||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.created": nowString,
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.url": "https://helm.sh",
|
||||
"org.opencontainers.image.authors": "John Snow (john@winterfell.com), Jane Snow",
|
||||
|
|
@ -133,6 +140,7 @@ func TestGenerateOCIChartAnnotations(t *testing.T) {
|
|||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.created": nowString,
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.source": "https://github.com/helm/helm",
|
||||
},
|
||||
|
|
@ -141,7 +149,7 @@ func TestGenerateOCIChartAnnotations(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
|
||||
result := generateChartOCIAnnotations(tt.chart, true)
|
||||
result := generateChartOCIAnnotations(tt.chart, nowString)
|
||||
|
||||
if !reflect.DeepEqual(tt.expect, result) {
|
||||
t.Errorf("%s: expected map %v, got %v", tt.name, tt.expect, result)
|
||||
|
|
@ -152,6 +160,8 @@ func TestGenerateOCIChartAnnotations(t *testing.T) {
|
|||
|
||||
func TestGenerateOCIAnnotations(t *testing.T) {
|
||||
|
||||
nowString := helmtime.Now().Format(time.RFC3339)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
chart *chart.Metadata
|
||||
|
|
@ -166,6 +176,7 @@ func TestGenerateOCIAnnotations(t *testing.T) {
|
|||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.created": nowString,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -183,6 +194,7 @@ func TestGenerateOCIAnnotations(t *testing.T) {
|
|||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.created": nowString,
|
||||
"extrakey": "extravlue",
|
||||
"anotherkey": "anothervalue",
|
||||
},
|
||||
|
|
@ -203,6 +215,7 @@ func TestGenerateOCIAnnotations(t *testing.T) {
|
|||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.created": nowString,
|
||||
"extrakey": "extravlue",
|
||||
},
|
||||
},
|
||||
|
|
@ -210,7 +223,7 @@ func TestGenerateOCIAnnotations(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
|
||||
result := generateOCIAnnotations(tt.chart, true)
|
||||
result := generateOCIAnnotations(tt.chart, nowString)
|
||||
|
||||
if !reflect.DeepEqual(tt.expect, result) {
|
||||
t.Errorf("%s: expected map %v, got %v", tt.name, tt.expect, result)
|
||||
|
|
@ -220,12 +233,16 @@ func TestGenerateOCIAnnotations(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGenerateOCICreatedAnnotations(t *testing.T) {
|
||||
|
||||
nowTime := helmtime.Now()
|
||||
nowTimeString := nowTime.Format(time.RFC3339)
|
||||
|
||||
chart := &chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
}
|
||||
|
||||
result := generateOCIAnnotations(chart, false)
|
||||
result := generateOCIAnnotations(chart, nowTimeString)
|
||||
|
||||
// Check that created annotation exists
|
||||
if _, ok := result[ocispec.AnnotationCreated]; !ok {
|
||||
|
|
@ -237,4 +254,22 @@ func TestGenerateOCICreatedAnnotations(t *testing.T) {
|
|||
t.Errorf("%s annotation with value '%s' not in RFC3339 format", ocispec.AnnotationCreated, result[ocispec.AnnotationCreated])
|
||||
}
|
||||
|
||||
// Verify default creation time set
|
||||
result = generateOCIAnnotations(chart, "")
|
||||
|
||||
// Check that created annotation exists
|
||||
if _, ok := result[ocispec.AnnotationCreated]; !ok {
|
||||
t.Errorf("%s annotation not created", ocispec.AnnotationCreated)
|
||||
}
|
||||
|
||||
if createdTimeAnnotation, err := helmtime.Parse(time.RFC3339, result[ocispec.AnnotationCreated]); err != nil {
|
||||
t.Errorf("%s annotation with value '%s' not in RFC3339 format", ocispec.AnnotationCreated, result[ocispec.AnnotationCreated])
|
||||
|
||||
// Verify creation annotation after time test began
|
||||
if !nowTime.Before(createdTimeAnnotation) {
|
||||
t.Errorf("%s annotation with value '%s' not configured properly. Annotation value is not after %s", ocispec.AnnotationCreated, result[ocispec.AnnotationCreated], nowTimeString)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,9 +216,12 @@ func initCompromisedRegistryTestServer() string {
|
|||
}
|
||||
|
||||
func testPush(suite *TestSuite) {
|
||||
|
||||
testingChartCreationTime := "1977-09-02T22:04:05Z"
|
||||
|
||||
// Bad bytes
|
||||
ref := fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)
|
||||
_, err := suite.RegistryClient.Push([]byte("hello"), ref, PushOptTest(true))
|
||||
_, err := suite.RegistryClient.Push([]byte("hello"), ref, PushOptCreationTime(testingChartCreationTime))
|
||||
suite.NotNil(err, "error pushing non-chart bytes")
|
||||
|
||||
// Load a test chart
|
||||
|
|
@ -229,20 +232,20 @@ func testPush(suite *TestSuite) {
|
|||
|
||||
// non-strict ref (chart name)
|
||||
ref = fmt.Sprintf("%s/testrepo/boop:%s", suite.DockerRegistryHost, meta.Version)
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptTest(true))
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptCreationTime(testingChartCreationTime))
|
||||
suite.NotNil(err, "error pushing non-strict ref (bad basename)")
|
||||
|
||||
// non-strict ref (chart name), with strict mode disabled
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptStrictMode(false), PushOptTest(true))
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptStrictMode(false), PushOptCreationTime(testingChartCreationTime))
|
||||
suite.Nil(err, "no error pushing non-strict ref (bad basename), with strict mode disabled")
|
||||
|
||||
// non-strict ref (chart version)
|
||||
ref = fmt.Sprintf("%s/testrepo/%s:latest", suite.DockerRegistryHost, meta.Name)
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptTest(true))
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptCreationTime(testingChartCreationTime))
|
||||
suite.NotNil(err, "error pushing non-strict ref (bad tag)")
|
||||
|
||||
// non-strict ref (chart version), with strict mode disabled
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptStrictMode(false), PushOptTest(true))
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptStrictMode(false), PushOptCreationTime(testingChartCreationTime))
|
||||
suite.Nil(err, "no error pushing non-strict ref (bad tag), with strict mode disabled")
|
||||
|
||||
// basic push, good ref
|
||||
|
|
@ -251,7 +254,7 @@ func testPush(suite *TestSuite) {
|
|||
meta, err = extractChartMeta(chartData)
|
||||
suite.Nil(err, "no error extracting chart meta")
|
||||
ref = fmt.Sprintf("%s/testrepo/%s:%s", suite.DockerRegistryHost, meta.Name, meta.Version)
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptTest(true))
|
||||
_, err = suite.RegistryClient.Push(chartData, ref, PushOptCreationTime(testingChartCreationTime))
|
||||
suite.Nil(err, "no error pushing good ref")
|
||||
|
||||
_, err = suite.RegistryClient.Pull(ref)
|
||||
|
|
@ -269,7 +272,7 @@ func testPush(suite *TestSuite) {
|
|||
|
||||
// push with prov
|
||||
ref = fmt.Sprintf("%s/testrepo/%s:%s", suite.DockerRegistryHost, meta.Name, meta.Version)
|
||||
result, err := suite.RegistryClient.Push(chartData, ref, PushOptProvData(provData), PushOptTest(true))
|
||||
result, err := suite.RegistryClient.Push(chartData, ref, PushOptProvData(provData), PushOptCreationTime(testingChartCreationTime))
|
||||
suite.Nil(err, "no error pushing good ref with prov")
|
||||
|
||||
_, err = suite.RegistryClient.Pull(ref)
|
||||
|
|
@ -281,12 +284,12 @@ func testPush(suite *TestSuite) {
|
|||
suite.Equal(ref, result.Ref)
|
||||
suite.Equal(meta.Name, result.Chart.Meta.Name)
|
||||
suite.Equal(meta.Version, result.Chart.Meta.Version)
|
||||
suite.Equal(int64(684), result.Manifest.Size)
|
||||
suite.Equal(int64(742), result.Manifest.Size)
|
||||
suite.Equal(int64(99), result.Config.Size)
|
||||
suite.Equal(int64(973), result.Chart.Size)
|
||||
suite.Equal(int64(695), result.Prov.Size)
|
||||
suite.Equal(
|
||||
"sha256:b57e8ffd938c43253f30afedb3c209136288e6b3af3b33473e95ea3b805888e6",
|
||||
"sha256:fbbade96da6050f68f94f122881e3b80051a18f13ab5f4081868dd494538f5c2",
|
||||
result.Manifest.Digest)
|
||||
suite.Equal(
|
||||
"sha256:8d17cb6bf6ccd8c29aace9a658495cbd5e2e87fc267876e86117c7db681c9580",
|
||||
|
|
@ -354,12 +357,12 @@ func testPull(suite *TestSuite) {
|
|||
suite.Equal(ref, result.Ref)
|
||||
suite.Equal(meta.Name, result.Chart.Meta.Name)
|
||||
suite.Equal(meta.Version, result.Chart.Meta.Version)
|
||||
suite.Equal(int64(684), result.Manifest.Size)
|
||||
suite.Equal(int64(742), result.Manifest.Size)
|
||||
suite.Equal(int64(99), result.Config.Size)
|
||||
suite.Equal(int64(973), result.Chart.Size)
|
||||
suite.Equal(int64(695), result.Prov.Size)
|
||||
suite.Equal(
|
||||
"sha256:b57e8ffd938c43253f30afedb3c209136288e6b3af3b33473e95ea3b805888e6",
|
||||
"sha256:fbbade96da6050f68f94f122881e3b80051a18f13ab5f4081868dd494538f5c2",
|
||||
result.Manifest.Digest)
|
||||
suite.Equal(
|
||||
"sha256:8d17cb6bf6ccd8c29aace9a658495cbd5e2e87fc267876e86117c7db681c9580",
|
||||
|
|
@ -370,7 +373,7 @@ func testPull(suite *TestSuite) {
|
|||
suite.Equal(
|
||||
"sha256:b0a02b7412f78ae93324d48df8fcc316d8482e5ad7827b5b238657a29a22f256",
|
||||
result.Prov.Digest)
|
||||
suite.Equal("{\"schemaVersion\":2,\"config\":{\"mediaType\":\"application/vnd.cncf.helm.config.v1+json\",\"digest\":\"sha256:8d17cb6bf6ccd8c29aace9a658495cbd5e2e87fc267876e86117c7db681c9580\",\"size\":99},\"layers\":[{\"mediaType\":\"application/vnd.cncf.helm.chart.provenance.v1.prov\",\"digest\":\"sha256:b0a02b7412f78ae93324d48df8fcc316d8482e5ad7827b5b238657a29a22f256\",\"size\":695},{\"mediaType\":\"application/vnd.cncf.helm.chart.content.v1.tar+gzip\",\"digest\":\"sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55\",\"size\":973}],\"annotations\":{\"org.opencontainers.image.description\":\"A Helm chart for Kubernetes\",\"org.opencontainers.image.title\":\"signtest\",\"org.opencontainers.image.version\":\"0.1.0\"}}",
|
||||
suite.Equal("{\"schemaVersion\":2,\"config\":{\"mediaType\":\"application/vnd.cncf.helm.config.v1+json\",\"digest\":\"sha256:8d17cb6bf6ccd8c29aace9a658495cbd5e2e87fc267876e86117c7db681c9580\",\"size\":99},\"layers\":[{\"mediaType\":\"application/vnd.cncf.helm.chart.provenance.v1.prov\",\"digest\":\"sha256:b0a02b7412f78ae93324d48df8fcc316d8482e5ad7827b5b238657a29a22f256\",\"size\":695},{\"mediaType\":\"application/vnd.cncf.helm.chart.content.v1.tar+gzip\",\"digest\":\"sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55\",\"size\":973}],\"annotations\":{\"org.opencontainers.image.created\":\"1977-09-02T22:04:05Z\",\"org.opencontainers.image.description\":\"A Helm chart for Kubernetes\",\"org.opencontainers.image.title\":\"signtest\",\"org.opencontainers.image.version\":\"0.1.0\"}}",
|
||||
string(result.Manifest.Data))
|
||||
suite.Equal("{\"name\":\"signtest\",\"version\":\"0.1.0\",\"description\":\"A Helm chart for Kubernetes\",\"apiVersion\":\"v1\"}",
|
||||
string(result.Config.Data))
|
||||
|
|
|
|||
25
pkg/time/ctime/ctime.go
Normal file
25
pkg/time/ctime/ctime.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package ctime
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Created(fi os.FileInfo) time.Time {
|
||||
return created(fi)
|
||||
}
|
||||
30
pkg/time/ctime/ctime_linux.go
Normal file
30
pkg/time/ctime/ctime_linux.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//go:build linux
|
||||
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package ctime
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func created(fi os.FileInfo) time.Time {
|
||||
st := fi.Sys().(*syscall.Stat_t)
|
||||
//nolint
|
||||
return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec))
|
||||
}
|
||||
27
pkg/time/ctime/ctime_other.go
Normal file
27
pkg/time/ctime/ctime_other.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//go:build !linux
|
||||
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package ctime
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func created(fi os.FileInfo) time.Time {
|
||||
return fi.ModTime()
|
||||
}
|
||||
Loading…
Reference in a new issue