GithubHelp home page GithubHelp logo

kubernetes / utils Goto Github PK

View Code? Open in Web Editor NEW
319.0 25.0 190.0 1.14 MB

Non-Kubernetes-specific utility libraries which are consumed by multiple projects.

License: Apache License 2.0

Go 97.24% Makefile 0.27% Shell 2.49%
kubernetes golang

utils's Introduction

Utils

Build Status GoDoc

A set of Go libraries that provide low-level, kubernetes-independent packages supplementing the Go standard libs.

Purpose

As Kubernetes grows and spins functionality out of its core and into cooperating repositories like apiserver, kubectl, kubeadm, etc., the need arises for leaf repositories to house shared code and avoid cycles in repository relationships.

This repository is intended to hold shared utilities with no Kubernetes dependencies that may be of interest to any Go project. See these instructions for moving an existing package to this repository.

Criteria for adding code here

  • Used by multiple Kubernetes repositories.

  • Complex enough to be worth vendoring, rather than copying (e.g. not 5 LOC).

  • Can be fully exercised by unit tests (e.g. no dependencies on kernels).

  • Has full unit test coverage.

  • Stable, or backward compatible, API, with complete godocs.

  • Go tools compliant (go get, go test, etc.).

  • Very few (ideally zero) external dependencies.

  • No dependencies on any other Kubernetes repository.

Contributing

Please see CONTRIBUTING.md for instructions on how to contribute.

utils's People

Contributors

andrewsykim avatar andyzhangx avatar aojea avatar apelisse avatar brendandburns avatar codenrhoden avatar cofyc avatar danwinship avatar dashpole avatar david-mcmahon avatar deads2k avatar dims avatar dixudx avatar gnufied avatar iancoolidge avatar ixdy avatar jingxu97 avatar jsafrane avatar k8s-ci-robot avatar liggitt avatar madhavjivrajani avatar mcrute avatar michaelbeaumont avatar mikedanese avatar msau42 avatar smarterclayton avatar somtochiama avatar sttts avatar thockin avatar vegemitecheesetoast avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

utils's Issues

mismatched types bool and klog.Verbose

../utils/trace/trace.go:100:57: invalid operation: stepThreshold == 0 || stepDuration > stepThreshold || klog.V(4) (mismatched types bool and klog.Verbose)
../utils/trace/trace.go:112:56: invalid operation: stepThreshold == 0 || stepDuration > stepThreshold || klog.V(4) (mismatched types bool and klog.Verbose)

ebtables: Fails to get ebtables version

What happened:
Kubelet fails to get ebtables version. Apparently some versions do not contain v before the version number.

# ebtables --version
ebtables 1.8.7 (nf_tables)
kubelet-wrapper[3583]: I0128 10:17:28.917365    3583 kubenet_linux.go:765] "Failed to get ebtables version. Skip syncing ebtables dedup rules" err="no ebtables version found in string: ebtables 1.8.3 (nf_tables)\n"

What you expected to happen:
versionMatcher should account for it.

How to reproduce it:
I reused the code snippet.

package main

import (
	"fmt"
	"regexp"
)

func getEbtablesVersionString(version string) (string, error) {
	versionMatcher := regexp.MustCompile(`v([0-9]+\.[0-9]+\.[0-9]+)`)
	match := versionMatcher.FindStringSubmatch(version)
	if match == nil {
		return "", fmt.Errorf("no ebtables version found in string: %s", version)
	}
	return match[1], nil
}

func main() {
	//version := "ebtables v2.0.10-4 (December 2011)"
	version := "ebtables 1.8.7 (nf_tables)"
	fmt.Printf(getEbtablesVersionString(version))
}

If version := "ebtables 1.8.7 (nf_tables)" then check fails

› ./main
%!(EXTRA *errors.errorString=no ebtables version found in string: ebtables 1.8.7 (nf_tables))

If version := "ebtables v2.0.10-4 (December 2011)", it goes through.

› ./main
2.0.10%!(EXTRA <nil>)

Anything else we need to know?:
Issue does not occur on Flatcar 2905.2.3 5.10.61-flatcar as the version string is "compliant" :)

$ ebtables --version
ebtables v2.0.10-4 (December 2011)

Environment:

  • Kubernetes version (use kubectl version):
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.5", GitCommit:"aea7bbadd2fc0cd689de94a54e5b7b758869d691", GitTreeState:"clean", BuildDate:"2021-09-15T21:10:45Z", GoVersion:"go1.16.8", Compiler:"gc", Platform:"linux/amd64"}
  • OS (e.g. from /etc/os-release): Flatcar Container Linux by Kinvolk 3033.2.0
  • Kernel (e.g. uname -a): 5.10.84-flatcar
  • Install tools:
  • Others:

Issues with Clock

utils/clock/clock.go

Lines 31 to 37 in 67b214c

type Clock interface {
PassiveClock
After(d time.Duration) <-chan time.Time
NewTimer(d time.Duration) Timer
Sleep(d time.Duration)
Tick(d time.Duration) <-chan time.Time
}

  1. After(d time.Duration) <-chan time.Time does not allow to stop the underlying timer to free resources if it's no longer needed. This can lead to memory leaks when a lot of long-lived timers pile up. It's a well-known pitfall. The fix is to remove the method and use the Timer() method instead.

  2. Tick(d time.Duration) <-chan time.Time has the same issue as After(). This method should be replaced with NewTicker() from the sister package (which I added here kubernetes/kubernetes#61976).

  3. Sleep(d time.Duration) should take context.Context to allow for early interruption of sleep. This method is used e.g. in client-go to sleep between request retries and often there is a context to signal that the attempt should be aborted. Currently there is no way to interrupt this sleep.

[mount] Addition of "checkAndRepairXfsFilesystem" inadvertently prevents XFS self-recovery via mounting

PR #126 added an extra step to run xfs_repair before mounting a XFS file system. However instead of helping to automatically correct FS issues due to prior unclean shutdowns, it actually prevented auto recovery from happening, which led to complete unavailability of the corresponding volume and subsequently required manual human intervention.

The sequence of events is as follows:

  1. A node loss / unclean shutdown occurs.
  2. A stateful pod is restarted on another healthy node; its volume is re-attached to the new node.
  3. xfs_repair is run against the volume. The relevant logs would look like these (in the context of rook-ceph but should apply to any other user of the mounter)
Filesystem corruption was detected for /dev/rbd1, running xfs_repair to repair
ID: 29 Req-ID: 0001-0009-rook-ceph-0000000000000001-9adb43bf-4e25-11ea-aa19-2ecc193be507 failed to mount device path (/dev/rbd1) to staging path (/var/lib/kubelet/plugins/kubernetes.io/csi/pv/pvc-423b5a86-7c03-43c4-a7e9-4921934016de/globalmount/0001-0009-rook-ceph-0000000000000001-9adb43bf-4e25-11ea-aa19-2ecc193be507) for volume (0001-0009-rook-ceph-0000000000000001-9adb43bf-4e25-11ea-aa19-2ecc193be507) error 'xfs_repair' found errors on device /dev/rbd1 but could not correct them: Phase 1 - find and verify superblock...

Phase 2 - using internal log
        - zero log...
ERROR: The filesystem has valuable metadata changes in a log which needs to
be replayed.  Mount the filesystem to replay the log, and unmount it before
re-running xfs_repair.  If you are unable to mount the filesystem, then use
the -L option to destroy the log and attempt a repair.
Note that destroying the log may cause corruption -- please attempt a mount
of the filesystem before doing this.
  1. The volume is then prevented from being mounted and manual intervention is required
  2. All that's needed then, is to manually mount the volume, which replay XFS logs automatically, then unmount it and restart the corresponding pod.

Note that step #5 was what has always happened prior to this change. The volume is simply mounted without any attempt to perform FS check / xfs_repair. It can simply correct itself as part of just being mounted, as per XFS design.

The recommended fix is to only attempt to run xfs_repair if mounting actually fails, as the last resort. There shouldn't be any need to xfs_repair prior to a mount failure.

Alternatively, don't bail out if an error occurs when running xfs_repair. Let the mount attempt happen anyway. It'll then either fix itself, or fail mounting with another error.

Relevant issue from rook-ceph repo: rook/rook#4914

CC'ing @27149chen

move kubernetes/kubernetes/tree/master/pkg/util/keymutex into utils

ceph-csi pulls https://github.com/kubernetes/kubernetes/tree/master/pkg/util/keymutex from kubernetes/kubernetes (https://github.com/ceph/ceph-csi/tree/master/vendor/k8s.io/kubernetes/pkg/util/keymutex) and now I would also like to use it in another CSI driver. The code itself is simple at first glance and might not cross the "complex enough to be worth vendoring" threshold, but it has had at least one bug in the past (kubernetes/kubernetes#65113). Sharing future maintenance in k8s.io/utils probably is better than other projects manually copying the code or vendoring kubernetes/kubernetes.

I discussed it with @msau42 on Slack (https://kubernetes.slack.com/conversation/C09R23FHP/p1544112842053400) and we agreed that it is worthwhile to ask here. If you agree that it is acceptable for k8s.io/utils, then I will proceed with preparing a PR.

investigate windows unit test failures

/kind feature
/help
/good-first-issue

See a log file here https://github.com/dims/utils/runs/576625118?check_suite_focus=true

2020-04-10T11:04:24.4738710Z === RUN   TestExecutorWithArgs
2020-04-10T11:04:24.4799121Z ##[error]    TestExecutorWithArgs: exec_test.go:83: expected success, got exec: "/bin/sh": file does not exist
2020-04-10T11:04:24.4803979Z ##[error]    TestExecutorWithArgs: exec_test.go:86: unexpected output: ""
2020-04-10T11:04:24.4804404Z --- FAIL: TestExecutorWithArgs (0.03s)
2020-04-10T11:04:24.4809272Z === RUN   TestTimeout
2020-04-10T11:04:24.4882836Z ##[error]    TestTimeout: exec_test.go:141: expected context deadline exceeded but got exit status 1
2020-04-10T11:04:24.4884120Z --- FAIL: TestTimeout (0.02s)
2020-04-10T11:04:24.4884602Z === RUN   TestSetEnv
2020-04-10T11:04:24.4903444Z ##[error]    TestSetEnv: exec_test.go:150: expected success, got exec: "/bin/sh": file does not exist
2020-04-10T11:04:24.4905427Z ##[error]    TestSetEnv: exec_test.go:153: unexpected output: ""
2020-04-10T11:04:24.4907085Z ##[error]    TestSetEnv: exec_test.go:160: expected success, got exec: "/bin/sh": file does not exist
2020-04-10T11:04:24.4918387Z ##[error]    TestSetEnv: exec_test.go:163: unexpected output: ""
2020-04-10T11:04:24.4936454Z --- FAIL: TestSetEnv (0.00s)
2020-04-10T11:04:24.4936662Z === RUN   TestStdIOPipes
2020-04-10T11:04:24.4937773Z ##[error]    TestStdIOPipes: exec_test.go:190: expected Start() not to error, got: exec: "/bin/sh": file does not exist
2020-04-10T11:04:24.4939120Z ##[error]    TestStdIOPipes: exec_test.go:194: expected StdOut to be 'OUT
2020-04-10T11:04:24.5021977Z         ', got: ''
2020-04-10T11:04:24.5023434Z ##[error]    TestStdIOPipes: exec_test.go:198: expected StdErr to be 'ERR
2020-04-10T11:04:24.5040816Z         ', got: ''
2020-04-10T11:04:24.5060348Z ##[error]    TestStdIOPipes: exec_test.go:202: expected Wait() not to error, got: exec: not started
2020-04-10T11:04:24.5062327Z --- FAIL: TestStdIOPipes (0.00s)
2020-04-10T11:04:24.5063358Z === RUN   ExampleNew_stderrPipe
2020-04-10T11:04:24.5063598Z panic: read |0: file already closed
2020-04-10T11:04:24.5063782Z 
2020-04-10T11:04:24.5064051Z goroutine 29 [running]:
2020-04-10T11:04:24.5064340Z k8s.io/utils/exec_test.ExampleNew_stderrPipe.func1(0x6561c0, 0xc0000c20c8, 0xc000166600)
2020-04-10T11:04:24.5064556Z 	D:/a/utils/utils/exec/stdiopipe_test.go:38 +0x12f
2020-04-10T11:04:24.5064817Z created by k8s.io/utils/exec_test.ExampleNew_stderrPipe
2020-04-10T11:04:24.5065021Z 	D:/a/utils/utils/exec/stdiopipe_test.go:35 +0x1e3
2020-04-10T11:04:24.5065245Z FAIL	k8s.io/utils/exec	0.291s
2020-04-10T11:04:37.3978462Z === RUN   TestTempDir
2020-04-10T11:04:37.3991282Z ##[error]    TestTempDir: dir_test.go:84: remove C:\Users\RUNNER~1\AppData\Local\Temp\prefix-754442531\ONE: The process cannot access the file because it is being used by another process.
2020-04-10T11:04:37.4032620Z --- FAIL: TestTempDir (0.01s)
2020-04-10T11:04:37.4033901Z FAIL
2020-04-10T11:04:37.4034495Z FAIL	k8s.io/utils/temp	0.049s
2020-04-10T11:04:39.5051288Z === RUN   TestTotalTime/Test_with_current_system_time
2020-04-10T11:04:39.5052091Z ##[error]    TestTotalTime/Test_with_current_system_time: trace_test.go:80: Expected total time 0, got 0 
2020-04-10T11:04:39.5052619Z --- FAIL: TestTotalTime (0.00s)
2020-04-10T11:04:39.5052868Z     --- FAIL: TestTotalTime/Test_with_current_system_time (0.00s)
2020-04-10T11:04:39.5055106Z === RUN   TestLogIfLong/When_threshold_is_500_and_msg_2_has_highest_share
2020-04-10T11:04:39.5055615Z ##[error]    TestLogIfLong/When_threshold_is_500_and_msg_2_has_highest_share: trace_test.go:230: Msg "msg2" expected in trace log: 
2020-04-10T11:04:39.5056022Z         
2020-04-10T11:04:39.5056298Z === RUN   TestLogIfLong/When_threshold_is_10_and_msg_3_has_highest_share
2020-04-10T11:04:39.5056772Z ##[error]    TestLogIfLong/When_threshold_is_10_and_msg_3_has_highest_share: trace_test.go:230: Msg "msg3" expected in trace log: 
2020-04-10T11:04:39.5057240Z         
2020-04-10T11:04:39.5057629Z === RUN   TestLogIfLong/When_threshold_is_0_and_all_msg_have_same_share
2020-04-10T11:04:39.5057928Z === RUN   TestLogIfLong/When_threshold_is_20_and_all_msg_1_has_highest_share
2020-04-10T11:04:39.5058149Z --- FAIL: TestLogIfLong (0.00s)
2020-04-10T11:04:39.5058394Z     --- FAIL: TestLogIfLong/When_threshold_is_500_and_msg_2_has_highest_share (0.00s)
2020-04-10T11:04:39.5058664Z     --- FAIL: TestLogIfLong/When_threshold_is_10_and_msg_3_has_highest_share (0.00s)
2020-04-10T11:04:39.5058976Z     --- PASS: TestLogIfLong/When_threshold_is_0_and_all_msg_have_same_share (0.00s)
2020-04-10T11:04:39.5059284Z     --- PASS: TestLogIfLong/When_threshold_is_20_and_all_msg_1_has_highest_share (0.00s)

Add presubmit check to prevent accidental go directive bump

Bumping the go directive in the go.mod files propagates to dependents virally, including kubernetes/kubernetes

Since this repo only releases from HEAD, we should be intentional when updating this directive since we may have to update on older kubernetes release branches.

Until golang/go#65573 is resolved and available, I'd suggest keeping the go directive in this repo no higher than the version of go reflected in the root go.mod file of the oldest supported kubernetes/kubernetes release branch (go1.20 on k/k 1.27 currently)

cc @MadhavJivrajani @dims

github CI flows do not run on all PRs

What happened:

PRs from new contributors do not auto-start github action CI flows, and PRs do not block on those flows

What you expected to happen:

The unit test and apidiff CI actions must pass before merge

How to reproduce it:

Seen in #216 and #215 which merged without apidiff / unit tests running

/assign @dims @thockin

integer.RoundToInt32 fails for 0.49999999999999994

If you add the following test to integer/integer_test.go:

{
  num: 0.49999999999999994,
  exp: 0,
}

And then run the tests, they fail:

go test ./integer
--- FAIL: TestRoundToInt32 (0.00s)
	integer_test.go:243: executing scenario 0
	integer_test.go:243: executing scenario 1
	integer_test.go:243: executing scenario 2
	integer_test.go:243: executing scenario 3
	integer_test.go:243: executing scenario 4
	integer_test.go:243: executing scenario 5
	integer_test.go:245: expected 0, got 1
	integer_test.go:243: executing scenario 6
FAIL
FAIL	github.com/kubernetes/utils/integer	0.006s

Is this beyond the scope of this function? If not I can submit a PR to fix this, though would it be acceptable to use math.Round provided in Go 1.10 and above?

IntervalClock should be deprecated in favor of SimpleIntervalClock

What happened:
I noticed that most of the methods of IntervalClock do a panic.

What you expected to happen:
I expected a supported and recommended type to not be full of methods that have no real implementation.

How to reproduce it:

Anything else we need to know?:

Environment:

  • Kubernetes version (use kubectl version):
  • OS (e.g. from /etc/os-release):
  • Kernel (e.g. uname -a):
  • Install tools:
  • Others:

@MadhavJivrajani
@kubernetes/sig-api-machinery-bugs

[mount] Do not print logs using klog directly, use an abstract log interface instead

I'm talking about the mount lib.
The lib is using klog to print the log message directly, it is ok in kubernetes and other projects which use klog too. But for projects which use different log tools (like logrus), since they may use different log format, it will be hard to maintain and collect logs.
So I'm asking if we can use an abstract log interface to print the log.

Bug with detect change file in file manager

Hi!

What happened:
i imported lib in my project to detect changes of file. when i started programm, i open file, change him, and press ctrl+s
events:

2022/08/25 13:09:00 event: IN_OPEN
2022/08/25 13:09:00 event: IN_CLOSE_WRITE
2022/08/25 13:09:00 event: IN_ATTRIB
2022/08/25 13:09:00 event: IN_DELETE_SELF
2022/08/25 13:09:00 event: IN_IGNORED

why detected last two events?

and in next time, when i change file, and press ctrl+s, watcher can not catch event.
i use Linux fedora 5.18.17-200
file opened in file manager (gnome)

but if i use IDE (visula studio code), after ctrl+s programm can detect any changes

may be I'm using the library incorrectly
What you expected to happen:
How to reproduce it:

Anything else we need to know?:

Environment:

  • Kubernetes version (use kubectl version):
  • OS (e.g. from /etc/os-release):
  • Kernel (e.g. uname -a):
  • Install tools:
  • Others:

Remove drive letter during formatAndMount causes subPath evalSymlink issue

In PR #166, we remove driver letter during formatAndMount due to the issue of driver letter runout. Now, volume mount will create symlink from volumeId to a path. e.g.,
mklink testlink \?\Volume{2f230ace-0000-0000-0000-010000000000}\

However, in subPath setup, https://github.com/kubernetes/kubernetes/blob/master/pkg/volume/util/subpath/subpath_windows.go#L53
filepath.EvalSymlinks(hostPath)
will fail with error: too many links.

We need to find another way to evaluate the symlink when driver letter is not assigned.

Include an example how to move a pkg to this repo

Finish the first util pkg moving and the document the steps in a separate file.

  • Copy the pkg to k8s.io/utils repo (need to preserve git history).
  • Move the pkg to the vendor directory and update godep.

Create a link in README.md to that doc.

Broken CI job: apidiff

The apidiff job is broken since the merge of #243

I don't think its anything to do with the PR itself, but more to do with how apidiff handles things.
I've filed an issue for this with a potential root cause: golang/go#60911

One of 2 can be done here:

  • We wait for the release branches to be bumped to go1.20 (which I think is in progress kubernetes/release#2815 (comment)) and then bump utils to go1.20 and then update sets to use comparable or apidiff to be fixed, whatever happens first.
  • Revert #243 till we bump to go1.20 or apidiff gets a fix that we can consume (whichever happens first).

IMO we should revert for now to ensure the CI is in good shape for when we need to fix something here to mitigate a test flake/failure in something like k/k.

Reverting isn't an option since we've already merged the API and apidiff will fail on a package level (ref).

/cc @logicalhan
/cc @cpanato (in case you have an estimate yet on the go1.20 bumps of release branches)

[mount] It is expensive to run fsck on every remount

In method formatAndMount, we will run fsck (or xfs_repair for xfs) before mounting the device. It happens on every remount, which is expensive for some kinds of disk that need more time to finish the command. But most of the time the filesystem is error free, so I think it will improve the perfermance if we can find a way to check the filesystem errors after mounting, and follow the steps:

  1. Mount the device.
  2. If it is failed, run fsck and mount again.
  3. If it is successful, check if the filesystem is running well, if not, unmount it and run fsck and then mount again.

[cinder-csi-driver] delete statefulset pod then new pod remounted failed with blkid returns invalid output

i deploy a statefulset with replicas one used cinder pvc ,then running
second,i delete the pod and the new replicas pod remount pvc but failed:

web-0 1/1 Running 0 4d23h 172.30.0.1 18.1.1.1
web-0 1/1 Terminating 0 4d23h 172.30.0.17 18.1.1.1
web-0 0/1 Terminating 0 4d23h 18.1.1.1
web-0 0/1 Pending 0 4s 18.1.1.1
web-0 0/1 ContainerCreating 0 4s 18.1.1.1

the web-0 pod logs :
Events:
Type Reason Age From Message

Normal Scheduled 2m8s default-scheduler Successfully assigned default/web-0 to 188.1.1.11
Normal SuccessfulAttachVolume 104s attachdetach-controller AttachVolume.Attach succeeded for volume "pvc-c9947920-991c-41cf-b08e-648ad67b6eb4"
Warning FailedMount 24s (x8 over 95s) kubelet, 188.1.1.11 MountVolume.MountDevice failed for volume "pvc-c9947920-991c-41cf-b08e-648ad67b6eb4" : rpc error: code = Internal desc = failed to get disk format of disk /dev/disk/by-id/virtio-c14a81a4-3d68-4288-9: blkid returns invalid output: /dev/disk/by-id/virtio-c14a81a4-3d68-4288-9: UUID="27b52caf-ec56-489e-91e6-4f3d003b43c2" TYPE="ext4"
Warning FailedMount 5s kubelet, 188.1.1.11 Unable to attach or mount volumes: unmounted volumes=[www]

the ds node-plugin logs :
I0611 09:04:39.526996 1 mount_linux.go:405] Attempting to determine if disk "/dev/disk/by-id/virtio-c14a81a4-3d68-4288-9" is formatted using blkid with args: ([-p -s TYPE -s PTTYPE -o export /dev/disk/by-id/virtio-c14a81a4-3d68-4288-9])
I0611 09:04:39.544355 1 mount_linux.go:408] Output: "/dev/disk/by-id/virtio-c14a81a4-3d68-4288-9: UUID="27b52caf-ec56-489e-91e6-4f3d003b43c2" TYPE="ext4"\n", err:
E0611 09:04:39.546005 1 utils.go:83] GRPC error: rpc error: code = Internal desc = failed to get disk format of disk /dev/disk/by-id/virtio-c14a81a4-3d68-4288-9: blkid returns invalid output: /dev/disk/by-id/virtio-c14a81a4-3d68-4288-9: UUID="27b52caf-ec56-489e-91e6-4f3d003b43c2" TYPE="ext4"

the k8s.io/utils/mount/mount_linux.go the function GetDiskFormat

the blkid command output error

1 mount_linux.go:407] dataOut: "/dev/disk/by-id/virtio-9fd0a671-3ebe-4dda-a: UUID="cce8468c-8f7e-4816-9ed0-81a83db4d7a1" TYPE="ext4"\n"

but the command blkid output is :
[root@paas-controller-1:/home/ubuntu]$ blkid -p -s TYPE -s PTTYPE -o export /dev/disk/by-id/virtio-9fd0a671-3ebe-4dda-a
DEVNAME=/dev/disk/by-id/virtio-9fd0a671-3ebe-4dda-a
TYPE=ext4

Create an OWNERS file

ref: kubernetes/community#1721
ref: kubernetes/community#2464

This is listed in sigs.yaml under sig-api-machinery... but with the comment:

- name: universal-utils # There is no reason why this is in api-machinery
      owners:
      - https://raw.githubusercontent.com/kubernetes/utils/master/OWNERS

/sig api-machinery
@kubernetes/sig-api-machinery-misc
/sig architecture
@kubernetes/sig-architecture-misc-use-only-as-a-last-resort

Which SIG, if not api-machinery, does this repo (or its individual packages) belong to?

Can we get an OWNERS file at the root of this repo?

/assign @apelisse @thockin @mengqiy @dashpole
as folks who've merged PR's here in the last year

/assign @lavalamp @deads2k
sig-api-machinery chairs

Move apimachinery/pkg/util/diff to utils

We use that package in unit tests for some of our operators to generate diffs between actual and expected CR objects. If you think it's a good idea to add it to this repo, I'm happy to help!

Feature request: Tagged releases

Is your feature request related to a problem? Please describe.

We use dependabot to keep our dependencies up to date and it seems like it's struggling with dependencies that don't have tags/releases.

Describe the solution you'd like in detail

Create tags and releases for this project.

Describe alternatives you've considered

Manually updating this dependency in all the projects.

Replace deprecated ioutil functions

Is your feature request related to a problem?/Why is this needed
This project uses the ioutil package for the following functions

ReadAll
TempDir
ReadFile
WriteFile
ReadDir

The functions in ioutil have been deprecated in favour of similar functions in io and os. While some of these deprecated functions internally call the corresponding, recommended, io and os functions, we could benefit by replacing these usages directly in the project code.

Describe the solution you'd like in detail
Replace usages of deprecated functions on a case-by-case basis with functions in os and io. The following command finds all files in the utils project importing the ioutils package:

grep -Rl "io/ioutil" .

and the result is

./temp/dir.go
./temp/dir_test.go
./io/read_test.go
./io/read.go
./inotify/inotify_linux_test.go
./exec/stdiopipe_test.go
./exec/exec_test.go
./mount/mount_windows_test.go
./mount/mount_linux_test.go
./mount/mount_helper_unix_test.go
./mount/safe_format_and_mount_test.go
./mount/mount_helper_test.go
./nsenter/nsenter_test.go

A simple MR replacing usages in these files with relevant (perhaps improved?) tests would be the solution. Almost all of the functions listed in the previous section have become wrappers around the newer functions. However, ioutil.ReadDir's replacement os.ReadDir offers supposedly better efficiency and the return type changes from fs.FileInfo -> fs.DirEntry and it returns a partial result in the case of an error while reading the directory. These changes do not matter in the context of this project and a simple replacement will work with no additional changes.

I've created a branch for this fix in my fork and go test ./... reports no errors

?   	k8s.io/utils/clock	[no test files]
ok  	k8s.io/utils/buffer	0.366s
ok  	k8s.io/utils/clock/testing	0.657s
?   	k8s.io/utils/inotify	[no test files]
?   	k8s.io/utils/internal/third_party/forked/golang/golang-lru	[no test files]
ok  	k8s.io/utils/cpuset	0.934s
ok  	k8s.io/utils/diff	1.485s
ok  	k8s.io/utils/env	1.206s
?   	k8s.io/utils/nsenter	[no test files]
ok  	k8s.io/utils/exec	4.072s
ok  	k8s.io/utils/exec/testing	1.685s
ok  	k8s.io/utils/field	1.661s
ok  	k8s.io/utils/integer	1.915s
ok  	k8s.io/utils/internal/third_party/forked/golang/net	2.192s
ok  	k8s.io/utils/io	2.250s
ok  	k8s.io/utils/keymutex	6.247s
ok  	k8s.io/utils/lru	7.261s
ok  	k8s.io/utils/mount	2.274s
ok  	k8s.io/utils/net	2.153s
ok  	k8s.io/utils/net/ebtables	2.368s
ok  	k8s.io/utils/path	2.328s
ok  	k8s.io/utils/pointer	2.272s
ok  	k8s.io/utils/ptr	1.672s
ok  	k8s.io/utils/semantic	1.706s
ok  	k8s.io/utils/set	1.696s
ok  	k8s.io/utils/strings	1.680s
ok  	k8s.io/utils/strings/slices	1.561s
ok  	k8s.io/utils/temp	1.647s
ok  	k8s.io/utils/temp/temptest	1.545s
ok  	k8s.io/utils/third_party/forked/golang/reflect	1.582s
ok  	k8s.io/utils/trace	1.598s

If this issue gets triaged, I can create a pull request.

Describe alternatives you've considered
N/A

Additional context
N/A

klog version v2 is not available yet

It seems utils is pointing to k8s.io/klog/v2, but this directory does not exist in any of the versions of klog. Please check https://github.com/kubernetes/klog.

This cause dep ensure to fail with an error

master: Could not introduce k8s.io/utils@master, as it requires package k8s.io/klog/v2 from k8s.io/klog, but in version v2.0.0 that package is missing.

FakeClock.Sleep calling FakeClock.Step may be unexpected

What happened:

Consider the following code (playground):

package main

import (
	"time"

	"k8s.io/utils/clock"
	clocktesting "k8s.io/utils/clock/testing"
)

func main() {
	clk := clocktesting.NewFakeClock(time.Now())
	go func(clk clock.Clock) {
		clk.Sleep(time.Second * 2)
		println("after 2 fakeclock seconds")
	}(clk)

	time.Sleep(time.Millisecond) // used to emulate race condition
	clk.Sleep(time.Second)
	println("after 1 fakeclock second")
}

The code above prints

after 2 fakeclock seconds
after 1 fakeclock second

When clk is changed to clock.RealClock{} (playground), the println lines are in correct order (1 -> 2).

What you expected to happen:

Intuitively, one would expect clk.Sleep(duration) to be identical to <-clk.After(duration). However, (*FakeClock).Sleep actually calls clk.Step instead, which immediately modifies the clock.

Suggested fix:

It would be a huge BC break to many testing packages if (*FakeClock).Sleep is changed to be passive. Documentation improvement would suffice, although deprecating Sleep would be great since Sleep doesn't always do what users intend. (imo, clk.Step is usually only intended in the main control flow of the unit test rather than other goroutines, so calling clk.Sleep from non-testing code is often unintended).

Alternatively, consider adding a ref-counted clock, where Sleep and After have identical behavior and the clock is only stepped when all refs are sleeping.

Environment:
k8s.io/utils: fe8a2dd

NewWatcher map panic

version: v0.0.0-20210527160623-6fdb442a123b

What happened:
k8s.io/utils/inotify.(*Watcher).AddWatch panic

What you expected to happen:
not crash

How to reproduce it:

Anything else we need to know?:

(dlv) bt
(dlv) goroutine 31
Switched from 97 to 31 (thread 21224)
(dlv) bt
 0  0x000000000046ac81 in runtime.raise
    at /mnt/go/go1.14/src/runtime/sys_linux_amd64.s:165
 1  0x0000000000466fc0 in runtime.systemstack_switch
    at /mnt/go/go1.14/src/runtime/asm_amd64.s:330
 2  0x0000000000436747 in runtime.fatalthrow
    at /mnt/go/go1.14/src/runtime/panic.go:1168
 3  0x0000000000436582 in runtime.throw
    at /mnt/go/go1.14/src/runtime/panic.go:1116
 4  0x0000000000414dac in runtime.mapaccess2_faststr
    at /mnt/go/go1.14/src/runtime/map_faststr.go:116
 5  0x0000000000883090 in k8s.io/utils/inotify.(*Watcher).AddWatch
    at /mnt/cache/go1.14/pkg/mod/k8s.io/[email protected]/inotify/inotify_linux.go:84
......
13  0x00000000004690d1 in runtime.goexit
    at /mnt/go/go1.14/src/runtime/asm_amd64.s:1373


(dlv) goroutine 97
Switched from 31 to 97 (thread 21224)
(dlv) bt
0  0x0000000000413fa1 in runtime.mapdelete_fast64
   at <autogenerated>:1
1  0x00000000008835ae in k8s.io/utils/inotify.(*Watcher).RemoveWatch
   at /mnt/cache/go1.14/pkg/mod/k8s.io/[email protected]/inotify/inotify_linux.go:128
.....
5  0x00000000004690d1 in runtime.goexit
   at /mnt/go/go1.14/src/runtime/asm_amd64.s:1373

maybe we need sync.map

Environment:

  • Kubernetes version (use kubectl version):
  • OS (e.g. from /etc/os-release):
  • Kernel (e.g. uname -a):
  • Install tools:
  • Others:

make clock support AfterFunc()

kubernetes/kubernetes still using k8s.io/apimachinery/pkg/util/clock because of clock does't implements AfterFunc yet.

We could implement AfterFunc in clock and replace k8s.io/apimachinery/pkg/util/clock with k8s.io/utils/clock in main repository.

No versionlist for this dependency / module - Renovate Dependency Bot fails on this dependency

Is your feature request related to a problem?/Why is this needed
I would like to manage this dependency in my cicd with renovate bot, but it does not work, as the @v/list endpoint is empty on goproxy.io

curl https://goproxy.io/k8s.io/utils/@v/list => empty response

Describe the solution you'd like in detail
It would be great, if versions would be released and visible in the versions list on goproxy.io

Describe alternatives you've considered
nothing, automatic version management and dependency bots are great. If you have other ideas, how this dependency could be updated automatically

Additional context
none

use lazy umount to avoid pod terminating problems caused by volume mounting dir accessed by other pods

  1. In K8s cluster with docker in /var/lib/docker and kubelet in /var/lib/kublet, create two pods as follows:
  • pod p1
cat <<EOF | kubectl create  -f -
apiVersion: v1
kind: Pod
metadata:
  name: p1
  namespace: default
spec:
  containers:
  - image: nginx:alpine
    name: nginx
    volumeMounts:
    - mountPath: /var-lib
      name: var-lib
  volumes:
  - hostPath:
      path: /var/lib
      type: Directory
    name: var-lib      
EOF
  • pod p2
cat <<EOF | kubectl create  -f -
apiVersion: v1
kind: Pod
metadata:
  name: p2
  namespace: default
spec:
  containers:
  - image: nginx:alpine
    name: nginx
EOF

get pod's uid: kubectl get po -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.uid}{"\n"}{end}'

  1. enter p1 and change to the secrete volume dir of p2:
#kubectl exec -it p1 /bin/sh
~ # cd /var-lib/kubelet/pods/{uid_of_p2}/volumes/kubernetes.io~secret/default-token-7q2gq/
/var-lib/kubelet/pods/{uid_of_p2}/volumes/kubernetes.io~secret/default-token-7q2gq # ls
ca.crt     namespace  token
/var-lib/kubelet/pods/{uid_of_p2}/volumes/kubernetes.io~secret/default-token-7q2gq #
  1. trying to delete p2 and p2 will be in terminating status always
# kubectl get po
NAME   READY   STATUS        RESTARTS   AGE
p1     1/1     Running       0          42m
p2     0/1     Terminating   0          42m

and log in kubelet:

Operation for "\"kubernetes.io/secret/045fa647-c544-4c94-ad69-0c80cd3166f0-default-token-7q2gq\" (\"045fa647-c544-4c94-ad69-0c80cd3166f0\")" failed. No retries permitted until 2020-05-12 16:39:20.400072347 +0800 CST m=+2364.992831733 (durationBeforeRetry 2m2s). Error: "UnmountVolume.TearDown failed for volume \"default-token-7q2gq\" (UniqueName: \"kubernetes.io/secret/045fa647-c544-4c94-ad69-0c80cd3166f0-default-token-7q2gq\") pod \"045fa647-c544-4c94-ad69-0c80cd3166f0\" (UID: \"045fa647-c544-4c94-ad69-0c80cd3166f0\") : unmount failed: exit status 32\nUnmounting arguments: /var/lib/kubelet/pods/045fa647-c544-4c94-ad69-0c80cd3166f0/volumes/kubernetes.io~secret/default-token-7q2gq\nOutput: umount: /var/lib/kubelet/pods/045fa647-c544-4c94-ad69-0c80cd3166f0/volumes/kubernetes.io~secret/default-token-7q2gq: target is busy.\n        (In some cases useful info about processes that use\n         the device is found by lsof(8) or fuser(1))\n"

Using ParseMountInfo to read /proc/self/mountinfo file.

Hi all

I wanted to check if we can use the ParseMountInfo to read /proc/self/mountinfo file?
I don't have the right environment to test this method out so wanted to check with the community if anyone can help.

The documentation says we need to provide /proc/<pid>/mountinfo as the file name.
Since I want to read /proc/self/mountinfo, can I provide this in the filename?

ParseMountInfo method for reference:

func ParseMountInfo(filename string) ([]MountInfo, error) {

Thank you

FakeExec is missing CommandContext

The FakeExec struct is missing an implementation of the CommandContext method which was added in this PR #27

func (fake *FakeExec) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd {
}

Create a SECURITY_CONTACTS file.

As per the email sent to kubernetes-dev[1], please create a SECURITY_CONTACTS
file.

The template for the file can be found in the kubernetes-template repository[2].
A description for the file is in the steering-committee docs[3], you might need
to search that page for "Security Contacts".

Please feel free to ping me on the PR when you make it, otherwise I will see when
you close this issue. :)

Thanks so much, let me know if you have any questions.

(This issue was generated from a tool, apologies for any weirdness.)

[1] https://groups.google.com/forum/#!topic/kubernetes-dev/codeiIoQ6QE
[2] https://github.com/kubernetes/kubernetes-template-project/blob/master/SECURITY_CONTACTS
[3] https://github.com/kubernetes/community/blob/master/committee-steering/governance/sig-governance-template-short.md

Configuring the amount of consistency reads

Is your feature request related to a problem? Please describe.

Currently, the consistency reads are hard-coded to 3:

maxListTries = 3

This gives three retries to read the file and if they are equal it succeeds.

utils/io/read.go

Lines 43 to 63 in 3b25d92

func consistentReadSync(filename string, attempts int, sync func(int)) ([]byte, error) {
oldContent, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
for i := 0; i < attempts; i++ {
if sync != nil {
sync(i)
}
newContent, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
if bytes.Compare(oldContent, newContent) == 0 {
return newContent, nil
}
// Files are different, continue reading
oldContent = newContent
}
return nil, InconsistentReadError{filename, attempts}
}

Describe the solution you'd like in detail

It would be nice to have an option to configure this setting, but I am not sure how.

Additional context

Maybe a bit of topic, but is this test even useful? Because it makes sense to test if the file changed. But if this is used to check if something else is currently getting mounted, before starting their own mount, is this even relevant? Because from the time this check passes until the actual mount happens, it is also possible that a mount happens in the meantime.

Or is there something I am missing?

Link in the inotify package is incorrect

What happened:

I visited the package documentation linked in the README and followed the link in the first section of the inotify package. That sent me to a site unrelated to Go or filesystems.

If I understand correctly, this domain is no longer used by the fsnotify project: fsnotify/fsnotify#407

For updates, see: https://fsnotify.org/

What you expected to happen:

I expected to land somewhere related to the fsnotify project or the gopkg.in/fsnotify.v0 package.

Move apimachinery/pkg/util/wait to utils

Could we move apimachinery/pkg/util/wait here? It has some really useful functions for waiting/polling/backoff that are fairly generic and are also well tested.

exec.Cmd.Stop() causes a panic when it tries to SIGKILL; fixing it is insufficient due to a data race

What happened:

If the command run by "utils/exec".New().Command() ignores SIGTERM and does not exit, or takes longer than 10 seconds to exit, using cmd.Stop() will result in a panic after 10 seconds.

What you expected to happen:

After SIGTERM fails to stop the process, Stop should send SIGKILL.

How to reproduce it:

I wrote a test that demonstrates the issue. It can be added to exec_test.go

func TestStopWhenSigtermIgnored(t *testing.T) {
  // Create a process that will ignore SIGTERM
  pr, pw, err := os.Pipe()
  if err != nil {
    t.Errorf("pipe: %v", err)
  }
  cmd := New().Command("/bin/sh", "-c", `trap "" TERM && echo "trap" && exec sleep 15`)
  cmd.SetStdin(nil)
  cmd.SetStdout(pw)
  cmd.SetStderr(pw)
  if err := cmd.Start(); err != nil {
    t.Errorf("expected Start() not to error, got: %v", err)
  }

  // Wait for SIGTERM's disposition to be set
  r := bufio.NewReader(pr)
  var line string
  if line, err = r.ReadString('\n'); err != nil {
    t.Errorf("ReadString: %v", err)
  }
  if line != "trap\n" {
    t.Errorf("unexpected write from shell script: %v", line)
  }

  // no race on process state after waiting 10 seconds to send SIGKILL
  cmd.Stop()
  if err := cmd.Wait(); err == nil || err.Error() != "signal: killed" {
    t.Errorf("expected Wait() to receive an error about a signal, got: %v", err)
  }
}
→ go test -v ./exec/ -test.run ^TestStopWhenSigtermIgnored$
=== RUN   TestStopWhenSigtermIgnored
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x1109a40]

goroutine 19 [running]:
os.(*ProcessState).exited(...)
	/usr/local/Cellar/go/1.20.6/libexec/src/os/exec_posix.go:83
os.(*ProcessState).Exited(...)
	/usr/local/Cellar/go/1.20.6/libexec/src/os/exec.go:155
k8s.io/utils/exec.(*cmdWrapper).Stop.func1()
	/Users/dsharp/workspace/kubernetes-utils/exec/exec.go:189 +0x20
created by time.goFunc
	/usr/local/Cellar/go/1.20.6/libexec/src/time/sleep.go:176 +0x32
FAIL	k8s.io/utils/exec	10.188s
FAIL

Anything else we need to know?:

The bug seems to come from a misconception about "os/exec".Cmd.ProcessState.Exited()

// Exited reports whether the program has exited.
// On Unix systems this reports true if the program exited due to calling exit,
// but false if the program terminated due to a signal.

However:

	// ProcessState contains information about an exited process.
	// If the process was started successfully, Wait or Run will
	// populate its ProcessState when the command completes.

That is, Cmd.ProcessState is nil until Cmd.Wait() successfully returns. Exited() really only is useful to distinguish if the process called exit() / returned normally, or was killed by a signal.


I also have a patch to address the panic:

 → git diff
diff --git a/exec/exec.go b/exec/exec.go
index d9c91e3..fd9ead2 100644
--- a/exec/exec.go
+++ b/exec/exec.go
@@ -186,7 +186,7 @@ func (cmd *cmdWrapper) Stop() {
        c.Process.Signal(syscall.SIGTERM)

        time.AfterFunc(10*time.Second, func() {
-               if !c.ProcessState.Exited() {
+               if c.ProcessState == nil {
                        c.Process.Signal(syscall.SIGKILL)
                }
        })

HOWEVER I'm concerned about a possible data race on the osexec.Cmd object between the time.AfterFunc goroutine and whichever routine calls cmd.Wait(). Running under the go race detector after the patch above has been applied does not show a race. But, if patched with the wrong condition (if c.ProcessState != nil {), then the race detector is triggered (It's not clear to me why the detection is sensitive to inverting the condition):

→ go test -race -count=1 -v ./exec/ -test.run ^TestStopWhenSigtermIgnored$
=== RUN   TestStopWhenSigtermIgnored
==================
WARNING: DATA RACE
Write at 0x00c0001b20a8 by goroutine 6:
  os/exec.(*Cmd).Wait()
      /usr/local/Cellar/go/1.20.6/libexec/src/os/exec/exec.go:894 +0x1b7
  k8s.io/utils/exec.(*cmdWrapper).Wait()
      /Users/dsharp/workspace/kubernetes-utils/exec/exec.go:157 +0x30
  k8s.io/utils/exec.TestStopWhenSigtermIgnored()
      /Users/dsharp/workspace/kubernetes-utils/exec/exec_test.go:160 +0x4ee
  testing.tRunner()
      /usr/local/Cellar/go/1.20.6/libexec/src/testing/testing.go:1576 +0x216
  testing.(*T).Run.func1()
      /usr/local/Cellar/go/1.20.6/libexec/src/testing/testing.go:1629 +0x47

Previous read at 0x00c0001b20a8 by goroutine 8:
  k8s.io/utils/exec.(*cmdWrapper).Stop.func1()
      /Users/dsharp/workspace/kubernetes-utils/exec/exec.go:189 +0x44

Goroutine 6 (running) created at:
  testing.(*T).Run()
      /usr/local/Cellar/go/1.20.6/libexec/src/testing/testing.go:1629 +0x805
  testing.runTests.func1()
      /usr/local/Cellar/go/1.20.6/libexec/src/testing/testing.go:2036 +0x8d
  testing.tRunner()
      /usr/local/Cellar/go/1.20.6/libexec/src/testing/testing.go:1576 +0x216
  testing.runTests()
      /usr/local/Cellar/go/1.20.6/libexec/src/testing/testing.go:2034 +0x87c
  testing.(*M).Run()
      /usr/local/Cellar/go/1.20.6/libexec/src/testing/testing.go:1906 +0xb44
  main.main()
      _testmain.go:71 +0x2e9

Goroutine 8 (finished) created at:
  time.goFunc()
      /usr/local/Cellar/go/1.20.6/libexec/src/time/sleep.go:176 +0x47
==================
    exec_test.go:161: expected Wait() to receive an error about a signal, got: <nil>
    testing.go:1446: race detected during execution of test
--- FAIL: TestStopWhenSigtermIgnored (15.02s)
=== NAME
    testing.go:1446: race detected during execution of test
FAIL
FAIL	k8s.io/utils/exec	15.218s
FAIL

If sending SIGKILL is to depend on the termination state of cmd (and it seems like it should), then there needs to be some kind of synchronization point between the timer.AfterFunc goroutine and the goroutine calling Wait().

It's possible that Stop() should be removed. I could only find one user of "utils/exec".Cmd.Stop() in public repositories by searching with sourcegraph, and that is in kubernetes/kubernetes/pkg/volume/flexvolume/driver-call.go. That code itself is lacking sufficient synchronization, and is in a deprecated component.
https://github.com/kubernetes/kubernetes/blob/99190634ab252604a4496882912ac328542d649d/pkg/volume/flexvolume/driver-call.go#L131

Environment:

  • Kubernetes version (use kubectl version): n/a
  • OS (e.g. from /etc/os-release): MacOS (dev environment)
  • Kernel (e.g. uname -a): Darwin
  • Install tools:
  • Others:

Setting-up the repository

There are a few things that needs to be done to get this repository up and rolling:

  • Have the root directory fully ready
    • README.md #10
    • Document how to move a pkg #11
    • Code of conduct #7
    • License #1
    • Document process? see items below
    • Issue and pull-request template
    • Verify boilerplate
    • what do I miss here?
  • test-infra: let's probably use Travis-ci #4
  • Do we need a readme per "package"? doc.go should do it.

This is missing a ton, feel free to edit that top-level comment.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.