Code Monkey home page Code Monkey logo

delta-kusto's Introduction

Continuous Build / Unit Test Exec tests

delta-kusto

Delta-Kusto

Delta-Kusto is a Command-line interface (CLI) enabling Continuous Integration / Continuous Deployment (CI / CD) automation with Kusto objects (e.g. tables, functions, policies, security roles, etc.) in Azure Data Explorer (ADX) databases. It can work on a single database, multiple databases, or an entire cluster. It also supports multi-tenant scenarios.

Delta-Kusto is doing what SQL Database projects do for Microsoft SQL: enabling CI/CD, change management and source control of Kusto databases. It works with and produces Kusto scripts so it doesn't require a new language / serialization format and can therefore be used with other tools of the ADX ecosystem.

Delta-Kusto runs on Linux, Mac OS & Windows as a stand-alone executable. It is meant to be used in headless mode.

Delta-Kusto works on database structure, not data:

  • Functions
  • Tables / Columns
  • Ingestion Mapping
  • Policies
  • Materialized Views
  • Security roles
  • External tables
  • Continuous Export

Documentations

See the documentation for details and tutorial section for different tutorials on Delta-Kusto.

Overview

The high-level view of delta-kusto is the following:

Overview diagram

The green boxes (current and target) represent sources. A source can be:

  • ADX Database
  • Kusto script

Delta-Kusto computes the delta between the two sources. The delta is a Kusto script containing the kusto commands required to take the current source and bring it to target source. The delta script can be exported as a stand alone file or as a folder hierarchy of scripts for easier readability and git-diff. It can also be applied to the current ADX Database.

Human validation often are required, especially if .drop commands are issued (to prevent unwanted data lost).

Scenarios / Flows

Using different combinations of sources can enable different scenarios:

Current Target Scenario Description
ADX Database Kusto scripts CI / CD scenario Push the state if a script set to an ADX database.
Kusto scripts ADX Database Determine gap between a script set and existing DB Computed Delta script shows what would need to be added to the script set to obtain the state of the target database.
Empty ADX Database Reverse engineer a database Special case of the previous scenario. The delta becomes the entire state of the target.
Kusto scripts Kusto scripts Offline sync Compute a delta between two script sets. No live databases needed.
ADX Database Kusto scripts Live Sync Find gap between two databases.

Limitations

The current release of Delta-Kusto includes functions, tables, ingestion mappings & the following policies:

  • Auto Delete
  • Caching
  • Ingestion Batching
  • Merge
  • Retention
  • Sharding
  • Update

See the list of issues for details on upcoming features and bugs found.

Alternatives

  • Sync Kusto - The Sync Kusto tool was built to help create a maintainable development process around Kusto.
  • Azure DevOps Task for Azure Data Explorer - Azure Pipelines task that enables you to create release pipelines and deploy your database changes to your Azure Data Explorer databases.

delta-kusto's People

Contributors

jensksuessmeyer avatar microsoft-github-policy-service[bot] avatar vplauzon 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

delta-kusto's Issues

Exception raised when Type is null

Get a Null Reference Exception inside DeltaKustoLib.KustoModel.MappingModel.MappingElement.Equals method when other.DataType is Null.

delta-kusto 0.10.2

Activating Client...
Client Activated
Newer clients available:  0.10.2.109
Loading parameters at 'XXX\rev-engineer-parameters.yaml'
2 jobs
Job 'download-dev':

Compute Delta...
829 commands in delta
Processing delta commands...
Delta processed / Job completed

Job 'delta-dev':

Compute Delta...
Exception registered with Session ID '142c3da0-d32f-4040-8f2f-ca94eef05edd'
Exception encountered:  System.NullReferenceException ; Object reference not set to an instance of an object.
Stack trace:     at DeltaKustoLib.KustoModel.MappingModel.MappingElement.Equals(Object obj) in C:\repos\delta-kusto\code\DeltaKustoLib\KustoModel\MappingModel.cs:line 79
   at System.Linq.Enumerable.SequenceEqual[TSource](IEnumerable`1 first, IEnumerable`1 second, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.SequenceEqual[TSource](IEnumerable`1 first, IEnumerable`1 second)
   at DeltaKustoLib.KustoModel.MappingModel.MappingAsJsonEquals(QuotedText otherMappingAsJson) in C:\repos\delta-kusto\code\DeltaKustoLib\KustoModel\MappingModel.cs:line 180
   at DeltaKustoLib.KustoModel.MappingModel.Equals(Object obj) in C:\repos\delta-kusto\code\DeltaKustoLib\KustoModel\MappingModel.cs:line 120
   at DeltaKustoLib.DeltaHelper.GetUpdated[M,K](IEnumerable`1 currents, IEnumerable`1 targets, Func`2 keyExtractor)+MoveNext() in C:\repos\delta-kusto\code\DeltaKustoLib\DeltaHelper.cs:line 50
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.ConcatIterator`1.MoveNext()
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.ConcatIterator`1.MoveNext()
   at DeltaKustoLib.KustoModel.TableModel.ComputeDelta(TableModel targetModel)+MoveNext() in C:\repos\delta-kusto\code\DeltaKustoLib\KustoModel\TableModel.cs:line 334
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at System.Linq.Enumerable.ConcatIterator`1.MoveNext()
   at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.SparseArrayBuilder`1.ReserveOrAdd(IEnumerable`1 items)
   at System.Linq.Enumerable.ConcatNIterator`1.LazyToArray()
   at System.Linq.Enumerable.ConcatNIterator`1.ToArray()
   at System.Collections.Immutable.ImmutableArray.CreateRange[T](IEnumerable`1 items)
   at DeltaKustoLib.KustoModel.DatabaseModel.ComputeDelta(DatabaseModel targetModel) in C:\repos\delta-kusto\code\DeltaKustoLib\KustoModel\DatabaseModel.cs:line 191
   at delta_kusto.DeltaOrchestration.ProcessJobAsync(MainParameterization parameters, IKustoManagementGatewayFactory kustoGatewayFactory, IFileGateway localFileGateway, String jobName, JobParameterization job) in C:\repos\delta-kusto\code\delta-kusto\DeltaOrchestration.cs:line 143
   at delta_kusto.DeltaOrchestration.ComputeDeltaAsync(String parameterFilePath, IEnumerable`1 pathOverrides) in C:\repos\delta-kusto\code\delta-kusto\DeltaOrchestration.cs:line 82
   at delta_kusto.DeltaOrchestration.ComputeDeltaAsync(String parameterFilePath, IEnumerable`1 pathOverrides) in C:\repos\delta-kusto\code\delta-kusto\DeltaOrchestration.cs:line 106
   at delta_kusto.DeltaOrchestration.ComputeDeltaAsync(String parameterFilePath, IEnumerable`1 pathOverrides) in C:\repos\delta-kusto\code\delta-kusto\DeltaOrchestration.cs:line 110
   at delta_kusto.Program.RunOptionsAsync(CommandLineOptions options) in C:\repos\delta-kusto\code\delta-kusto\Program.cs:line 120
   at CommandLine.ParserResultExtensions.WithParsedAsync[T](ParserResult`1 result, Func`2 action)
   at delta_kusto.Program.Main(String[] args) in C:\repos\delta-kusto\code\delta-kusto\Program.cs:line 51

Was able to work around it with this "fix":

public override bool Equals(object? obj)
{
    var other = obj as MappingElement;
    var result = other != null
        && other.Column.Equals(Column)
        && other.DataType != null && other.DataType.Equals(DataType)
        && other.Properties.Equals(Properties);

    return result;
}

Can't Parse Whitespace

I am observing an issue with parsing or trimming parts of our schema that seem to be connected to whitespace or empty lines. I am looking at having our team adjust their usage to try and mitigate this issue-- our lead Kusto engineer thinks we should replace occurrences of empty lines with whitespace to //, but in the longer term it will be important for us to have this issue mitigated within the application if possible.

Here is a link to the occurrence of the error:
https://msazure.visualstudio.com/One/_releaseProgress?_a=release-environment-logs&releaseId=6305502&environmentId=22941103

Are you able to confirm if there is a better way our team can write their functions that would eliminate this error?

Missing 'Azure.Identity' dependency

Hi,
When running with MSI enabled I get the following error:

Exception encountered: System.IO.FileNotFoundException ; 
Could not load file or assembly 'Azure.Identity, Version=1.3.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8'. 
The system cannot find the file specified.

Version: 0.8.0.98

log trace:

$ /bin/delta-kusto -p /tmp/job-2258241656.yaml



delta-kusto 0.8.0.98


Activating Client...
Client Activated
Newer clients available: 0.8.0.98
Loading parameters at '/tmp/job-2258241656.yaml'
1 jobs
Job 'push-db1937-to-prod':


Error: Issue in running job 'push-db1937-to-prod'
Error: Issue running Kusto script '.show database schema as csl script' on cluster 'https://mcasrs02euw1jony.westeurope.kusto.windows.net/' / database 'db1937'
Exception encountered: System.IO.FileNotFoundException ; Could not load file or assembly 'Azure.Identity, Version=1.3.0.0, Culture=neutral, PublicKeyToken=****'. The system cannot find the file specified.


Stack trace: at Kusto.Cloud.Platform.Aad.AadManagedIdentityTokenCredentialsProvider..ctor(String nameForTraces)
at Kusto.Cloud.Platform.Aad.AadManagedIdentityTokenCredentialsProvider.CreateProviderFromString(String mi)
at Kusto.Data.Security.HttpClientAuthenticatorFactory.CreateAuthenticator(KustoConnectionStringBuilder kcsb)
at Kusto.Data.Net.Client.RestClient2.MakeHttpRequestAsyncImpl(String address, String csl, String ns, String databaseName, Boolean streaming, ClientRequestProperties properties, ServiceModelTimeoutKind timeoutKind, String clientRequestId, Stream body, StreamProperties streamProperties)
at Kusto.Cloud.Platform.Utils.MonitoredActivity.InvokeAsync[TActivityType,TResult](TActivityType activityType, Func`1 func, String clientRequestId)
at Kusto.Cloud.Platform.Utils.MonitoredActivity.InvokeAsync[TActivityType,TResult](TActivityType activityType, Func`1 func, String clientRequestId)
at Kusto.Data.Net.Client.RestClient2.MakeHttpRequestAsync(ActivityType activityType, String baseAddress, String relativeAddress, String clientRequestIdPrefix, String ns, String databaseName, String csl, String addr, Boolean streaming, ClientRequestProperties properties, ServiceModelTimeoutKind timeoutKind, StreamProperties streamProperties)
at Kusto.Data.Net.Client.RestClient2.ExecuteControlCommandAsync(String databaseName, String command, String addr, ClientRequestProperties properties)
at DeltaKustoIntegration.Kusto.KustoManagementGateway.<>c__DisplayClass10_0.<<ExecuteCommandAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Polly.Retry.AsyncRetryEngine.ImplementationAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, ExceptionPredicates shouldRetryExceptionPredicates, ResultPredicates`1 shouldRetryResultPredicates, Func`5 onRetryAsync, Int32 permittedRetryCount, IEnumerable`1 sleepDurationsEnumerable, Func`4 sleepDurationProvider, Boolean continueOnCapturedContext)
at Polly.AsyncPolicy.ExecuteAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext)
at DeltaKustoIntegration.Kusto.KustoManagementGateway.ExecuteCommandAsync(String commandScript, CancellationToken ct)
Exception encountered: DeltaKustoLib.DeltaException ; Issue running Kusto script '.show database schema as csl script' on cluster 'https://mcasrs02euw1jony.westeurope.kusto.windows.net/' / database 'db1937'
Stack trace: at DeltaKustoIntegration.Kusto.KustoManagementGateway.ExecuteCommandAsync(String commandScript, CancellationToken ct)
at DeltaKustoIntegration.Kusto.KustoManagementGateway.DeltaKustoIntegration.Kusto.IKustoManagementGateway.ReverseEngineerDatabaseAsync(CancellationToken ct)
at DeltaKustoIntegration.Database.KustoDatabaseProvider.DeltaKustoIntegration.Database.IDatabaseProvider.RetrieveDatabaseAsync(CancellationToken ct)
at delta_kusto.DeltaOrchestration.RetrieveDatabaseAsync(IDatabaseProvider currentDbProvider, String db)
at delta_kusto.DeltaOrchestration.ProcessJobAsync(MainParameterization parameters, IKustoManagementGatewayFactory kustoGatewayFactory, IFileGateway localFileGateway, String jobName, JobParameterization job)
Exception encountered: System.IO.FileNotFoundException ; Could not load file or assembly 'Azure.Identity, Version=1.3.0.0, Culture=neutral, PublicKeyToken=****'. The system cannot find the file specified.

External Tables are not Detected

When we tried to move the created External Table from a lower environment to a higher environment we noticed that the created external table is not shown in the generated state file as well as the Delta file

Database Sharding Policy in Reverse Engineer

When downloading a schema as a .kql file we obtain its database policy sharding commands, i.e.

.alter database ['demo'] policy sharding
{
  "MaxRowCount": 750000,
  "MaxExtentSizeInMb": 1024,
  "MaxOriginalSizeInMb": 2048,
  "UseShardEngine": true,
  "ShardEngineMaxRowCount": 1048576,
  "ShardEngineMaxExtentSizeInMb": 8192,
  "ShardEngineMaxOriginalSizeInMb": 3072
}

It seems that the line "UseShardEngine": true, is somewhat useless-- according to the public documentation we're consulting this value cannot be changed after database creation, and trying to reverse engineer it into any existing database where the 'UseShardEngine' value is different than what is in the schema causes the following error:

    "error": {
        "code": "BadRequest",
        "message": "Request is invalid and cannot be executed.",
        "@type": "Kusto.DataNode.Exceptions.InvalidPolicyOperationException",
        "@message": "Error during execution of a policy operation: UseShardEngine can not be set, if shard engine is not enabled",
        "@context": {
            "timestamp": "2022-05-18T18:37:18.5938655Z",
            "serviceAlias": "BALROGDEVKUSTO",
            "machineName": "KEngine000000",
            "processName": "Kusto.WinSvc.Svc",
            "processId": 3548,
            "threadId": 704,
            "appDomainName": "Kusto.WinSvc.Svc.exe",
            "clientRequestId": "KE.RunCommand;dcd356bb-eb5a-45e7-8ee1-23f0280c787e",
            "activityId": "49d01113-e782-4bd9-b344-4a3933b3ad48",
            "subActivityId": "742ab8d1-ba5f-4db2-a652-0d5da42ea1bf",
            "activityType": "DN.AdminCommand.DatabaseScopedDataShardingPolicyAlterCommand",
            "parentActivityId": "32aef848-6f6a-487f-829e-ae71a2bfb7d1",
            "activityStack": "(Activity stack: CRID=KE.RunCommand;dcd356bb-eb5a-45e7-8ee1-23f0280c787e ARID=49d01113-e782-4bd9-b344-4a3933b3ad48 > DN.Admin.Client.ExecuteControlCommand/8375c0e8-e25c-4788-9821-893c959482a9 > P.WCF.Service.ExecuteControlCommand..IInterNodeCommunicationAdminContract/232e1f1a-896c-429e-b56e-d9383db8f1da > DN.FE.ExecuteControlCommand/32aef848-6f6a-487f-829e-ae71a2bfb7d1 > DN.AdminCommand.DatabaseScopedDataShardingPolicyAlterCommand/742ab8d1-ba5f-4db2-a652-0d5da42ea1bf)"
        },
        "@permanent": true
    }
}

We have automated our pipeline to comment out the command in the schema, but I think it would be a good idea to simply make the delta kusto reverse engineer command suppress this error into a warning and continue, as there does not seem to be a scenario where issuing the command at all would accomplish anything.

Inherit table level comment (metadata) from delta table to a view

I am looking for options on how can I inherit the table level comment directly into a view when it is pointing to that delta table.
I do see we can add comment to the view explicitly with the command but not able to find anything where we can inherit the comment from the table itself automatically instead of manually adding it.

Functions with inline functions

A function containing an inline function doesn't work.

Error message: "There should be one-and-only-one Function declaration but there are 2"

MSI Authentication

Token acquisition through Managed Service Identity (MSI) authentication isn't available yet.

This would allow to run Delta Kusto without sharing Service Principal credentials. It would require Delta Kusto to run in a compute having MSI enabled.

Exception thrown when parsing function when using Let and Distinct commands

I have a function that is using logic like this:

.create-or-alter function testfunction() { let temp = random_table_with_timestamp | distinct timestamp=bin(timestamp,1m); temp }

When the application attempts to parse the above command it craps out. Debugging a bit, it appears to be an issue with distinct with a column name assignment and/or the bin() function. Regardless, I can work around this by refactoring my code, but has to download code and test locally before I could figure out what the issue was. If other people try to use this and have similar syntax, it breaks. Yes, there are many ways to re-write this query so that I don't have the assignment or the bin function with distinct.. But if it works in Kusto Explorer and on ADX, I think it should work when being valid when parsing.

Fix is probably going to be an upgrade to the Microsoft.Azure.Kusto.Language package...

Full StackTrace

System.InvalidOperationException
HResult=0x80131509
Message=Function body was expected to be surrounded by curly brace but isn't: { let temp = random_table_with_timestamp | distinct timestamp=bin(timestamp,1m
Source=DeltaKustoLib
StackTrace:
at DeltaKustoLib.CommandModel.CreateFunctionCommand.TrimFunctionSchemaBody(String body) in C:\Users\bob\Source\Repos\delta-kusto\code\DeltaKustoLib\CommandModel\CreateFunctionCommand.cs:line 262
at DeltaKustoLib.CommandModel.CreateFunctionCommand.FromCode(SyntaxElement rootElement) in C:\Users\bob\Source\Repos\delta-kusto\code\DeltaKustoLib\CommandModel\CreateFunctionCommand.cs:line 66
at DeltaKustoLib.CommandModel.CommandBase.CreateCommand(String script, KustoCode code, Boolean ignoreUnknownCommands) in C:\Users\bob\Source\Repos\delta-kusto\code\DeltaKustoLib\CommandModel\CommandBase.cs:line 104
at DeltaKustoLib.CommandModel.CommandBase.ParseAndCreateCommand(String script, Boolean ignoreUnknownCommands) in C:\Users\bob\Source\Repos\delta-kusto\code\DeltaKustoLib\CommandModel\CommandBase.cs:line 65

This exception was originally thrown at this call stack:
DeltaKustoLib.CommandModel.CreateFunctionCommand.TrimFunctionSchemaBody(string) in CreateFunctionCommand.cs
DeltaKustoLib.CommandModel.CreateFunctionCommand.FromCode(Kusto.Language.Syntax.SyntaxElement) in CreateFunctionCommand.cs
DeltaKustoLib.CommandModel.CommandBase.CreateCommand(string, Kusto.Language.KustoCode, bool) in CommandBase.cs
DeltaKustoLib.CommandModel.CommandBase.ParseAndCreateCommand(string, bool) in CommandBase.cs

libicu requirements for distress containers

Hi,
I'm trying to run delta-kusto in a minimal distress container.

and got the following error when running delta-kusto:

Couldn't find a valid ICU package installed on the system. Please install libicu using your package manager and try again. Alternatively you can set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support. 
Please see https://aka.ms/dotnet-missing-libicu for more information.

I tried running again with export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 as suggested but hit the following error:

Unhandled exception. System.Globalization.CultureNotFoundException: Only the invariant culture is supported in globalization-invariant mode. 
See https://aka.ms/GlobalizationInvariantMode for more information. (Parameter 'name')
en-us is an invalid culture identifier

Can you please confirm if libicu installation is indeed required or if a different solution exists for this.

The docker build:

FROM gcr.io/distroless/cc-debian11:nonroot-debug
WORKDIR /
COPY --from=builder /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libz.so.1
COPY --from=builder /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/x86_64-linux-gnu/libgcc_s.so.1
COPY --from=builder /tmp/delta-kusto /bin/

(I added lib and libgcc as they showed up as missing when I ran ldd on delta-kusto - will not start without them...)

Add CSV & JSON outputs

By having each command as a row. Other than the command itself, other columns could contain values that allow for easy filtering: folder, command-type, user, etc. .

Failed to parse key="value" in an override such as secret when secret is containing equal sign

Hello,

Code in InplaceOverride method in https://github.com/microsoft/delta-kusto/blob/main/code/DeltaKustoIntegration/Parameterization/ParameterOverrideHelper.cs is checking for the proper number of equal signs in an override but doesn't properly support the value containing an equal sign by itself and throws an error:

public static void InplaceOverride(object target, IEnumerable<string> pathOverrides)
        {
            if (pathOverrides.Any())
            {
                try
                {
                    var splits = pathOverrides.Select(t => t.Split('='));
                    var noEquals = splits.FirstOrDefault(s => s.Length != 2);

                    if (noEquals != null)
                    {
                        throw new DeltaException(
                            $"Override must be of the form path=value ; "
                            + $"exception:  '{string.Join('=', noEquals)}'");
                    }

So, let's say you want to override the secret parameter and the secret is containing an equal sign, it'll break:

Activating Client...                                                                                                                                                                    
Client Activated                                                                                                                                                                        
Newer clients available:  0.10.2.109                                                                                                                                                    
Loading parameters at 'C:\XXX\rev-engineer-parameters.yaml'                                                                                    
Error:  Issue with the following parameter override:  'System.String[]'                                                                                                                 
  Error:  Override must be of the form path=value ; exception:  'tokenProvider.login.secret=LXXXXX4='                                               
  Exception encountered:  DeltaKustoLib.DeltaException ; Override must be of the form path=value ; exception:  'tokenProvider.login.secret=LXXXXX4='
  Stack trace:     at DeltaKustoIntegration.Parameterization.ParameterOverrideHelper.InplaceOverride(Object target, IEnumerable`1 pathOverrides)

Whereas what is passed is: tokenProvider.login.secret="LXXXXX4="
Which should be considered as a valid key/value override.

Executable Does Not Return Error in Powershell

I'm using this program to automate synchronization of our clusters to source control. I am finding that deltakusto.exe does not actually return an error to the console when it encounters an error.

If it logs an error, the $LASTEXITCODE of powershell is not modified nor does it return any error codes. The only way to determine if it ran successfully or not seems to be to print its output to a file and scan it for error codes.

Mac OS comptability

can you please provide a description how (if) this utility can be used from macos systems

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.